From a99853f991489908a22494ab7e0e3bec4cfb496b Mon Sep 17 00:00:00 2001 From: rizemon Date: Thu, 30 Sep 2021 10:13:21 +0800 Subject: [PATCH 001/607] Set theme jekyll-theme-cayman --- docs/_config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/_config.yml diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..c4192631f2 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file From e62e9cd6452b5bfefd0569f06b6fc0b0684f05a1 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 30 Sep 2021 10:36:28 +0800 Subject: [PATCH 002/607] Update AboutUs --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..9c7e4572a9 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,7 +2,7 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://avatars.githubusercontent.com/u/16810847?v=4) | John Doe | [Github](https://github.com/arraysius/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) From badf7c67bc458b4f7d92552030205641bcfcd7b7 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 30 Sep 2021 10:36:34 +0800 Subject: [PATCH 003/607] Updated AboutUs.md for myself --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..d246d44a27 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,6 +4,6 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: ![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Sim Tian Boon | [Github](https://github.com/Kair0s3/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) From c09e025eedc6b3a7bec77eb0d68ec35766c436fe Mon Sep 17 00:00:00 2001 From: rizemon Date: Thu, 30 Sep 2021 10:36:44 +0800 Subject: [PATCH 004/607] Update AboutUs.md --- docs/AboutUs.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..9ab221669c 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,8 +2,9 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://avatars.githubusercontent.com/u/26685970?v=4) | Tan Jia Le | [Github](https://github.com/rizemon) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) + From b84edb5af48318cedc402f1b039cd63ebec3dc7a Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Thu, 30 Sep 2021 10:37:01 +0800 Subject: [PATCH 005/607] Update AboutUs - Update AboutUs.md --- docs/AboutUs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..999ae346ff 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -3,7 +3,7 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: ![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://media.discordapp.net/attachments/285692337357586432/892962858546114560/photo_2021-05-07_15-04-25.jpg?text=BreadDog) | Sean Phang | [Github](https://github.com/SeenFang) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) \ No newline at end of file From acf031a682ea303894c4c4e420ee85644622660c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 30 Sep 2021 10:37:30 +0800 Subject: [PATCH 006/607] Revert "Updated AboutUs.md for myself" This reverts commit badf7c67bc458b4f7d92552030205641bcfcd7b7. --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index d246d44a27..0f072953ea 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,6 +4,6 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: ![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Sim Tian Boon | [Github](https://github.com/Kair0s3/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) From 7dae5eb91cbc435e819c3b260d5666e2381ff85f Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 30 Sep 2021 10:38:14 +0800 Subject: [PATCH 007/607] Updated AboutUs for myself. again... --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..d246d44a27 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,6 +4,6 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: ![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Sim Tian Boon | [Github](https://github.com/Kair0s3/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) ![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) From 966874fed1da561f34b85b8b3852b68bceaebaec Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 1 Oct 2021 19:43:13 +0800 Subject: [PATCH 008/607] Skeletal Commit for tP Skeletal base for team to start on their respective features ("commands") implementation. --- src/main/java/seedu/duke/Duke.java | 36 +++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 5c74e68d59..6d227a9d37 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -3,19 +3,39 @@ import java.util.Scanner; public class Duke { + /** + * Logo to be changed - possibly to a welcome message instead for Decodex. + */ + private static final String LOGO = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + + /** + * Command Prefix Constants + */ + private static final String EXIT_COMMAND = "exit"; + /** * Main entry-point for the java.duke.Duke application. */ public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); + System.out.println("Hello from\n" + LOGO); Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); + while (true) { + System.out.print("Decodex > "); + String userInput = in.nextLine(); + + switch (userInput) { + case EXIT_COMMAND: + System.out.println("Goodbye!"); + System.exit(0); + default: + // Skeletal - Just "echos" back to us. + System.out.println(userInput); + } + } } } From bd0a4bc77d59b4b5871c6cd443321a8d25c89f4b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 1 Oct 2021 19:43:52 +0800 Subject: [PATCH 009/607] Updated the text-ui-test with respect to the changes to the tP code. --- text-ui-test/EXPECTED.TXT | 7 +++++-- text-ui-test/input.txt | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..4679efac27 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,5 +5,8 @@ Hello from | |_| | |_| | < __/ |____/ \__,_|_|\_\___| -What is your name? -Hello James Gosling +Decodex > whoami +Decodex > bring me thanos +Decodex > hi +Decodex > bye +Decodex > Goodbye! \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f95..4c09ae32bc 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1,5 @@ -James Gosling \ No newline at end of file +whoami +bring me thanos +hi +bye +exit \ No newline at end of file From c40105542c4a0ba9398fdd6fd852f5a037a9921f Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 1 Oct 2021 19:56:13 +0800 Subject: [PATCH 010/607] Additional updates to fix the conflicts with gradle's checkStyleMain --- src/main/java/seedu/duke/Duke.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 6d227a9d37..fe4272df02 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -13,7 +13,7 @@ public class Duke { + "|____/ \\__,_|_|\\_\\___|\n"; /** - * Command Prefix Constants + * Command Prefix Constants. */ private static final String EXIT_COMMAND = "exit"; @@ -31,7 +31,7 @@ public static void main(String[] args) { switch (userInput) { case EXIT_COMMAND: System.out.println("Goodbye!"); - System.exit(0); + break; default: // Skeletal - Just "echos" back to us. System.out.println(userInput); From 01a876471b1a0e1ebddea437e3e4683c1d03fcff Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 1 Oct 2021 20:04:33 +0800 Subject: [PATCH 011/607] Fixing another issue (logic-related) during Gradle checks in Github --- src/main/java/seedu/duke/Duke.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index fe4272df02..7488a1fea8 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -31,6 +31,7 @@ public static void main(String[] args) { switch (userInput) { case EXIT_COMMAND: System.out.println("Goodbye!"); + System.exit(0); break; default: // Skeletal - Just "echos" back to us. From 0c3f9b6a0060db05acd7d38d01e8f8eb2c7e0737 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 1 Oct 2021 20:11:37 +0800 Subject: [PATCH 012/607] Yet another update cause of gradle checks. Kindda annoying, the local text-ui-test has no issues but the github action's one throws error for it. --- src/main/java/seedu/duke/Duke.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 7488a1fea8..16a502c4d6 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -30,7 +30,7 @@ public static void main(String[] args) { switch (userInput) { case EXIT_COMMAND: - System.out.println("Goodbye!"); + System.out.print("Goodbye!"); System.exit(0); break; default: From a69fe6eece928b1f24a17c3359ffc5aded9ff0b7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 4 Oct 2021 16:49:17 +0800 Subject: [PATCH 013/607] Add project structure --- build.gradle | 4 ++-- .../java/{seedu/duke/Duke.java => decodex/Decodex.java} | 6 +++--- src/main/java/decodex/commands/Command.java | 5 +++++ src/main/java/decodex/data/Data.java | 5 +++++ src/main/java/decodex/data/exception/DummyException.java | 5 +++++ src/main/java/decodex/modules/BaseModule.java | 5 +++++ src/main/java/decodex/parser/Parser.java | 5 +++++ src/main/java/decodex/storage/Storage.java | 5 +++++ src/main/java/decodex/ui/Ui.java | 5 +++++ src/test/java/{seedu/duke => decodex}/DukeTest.java | 4 ++-- 10 files changed, 42 insertions(+), 7 deletions(-) rename src/main/java/{seedu/duke/Duke.java => decodex/Decodex.java} (90%) create mode 100644 src/main/java/decodex/commands/Command.java create mode 100644 src/main/java/decodex/data/Data.java create mode 100644 src/main/java/decodex/data/exception/DummyException.java create mode 100644 src/main/java/decodex/modules/BaseModule.java create mode 100644 src/main/java/decodex/parser/Parser.java create mode 100644 src/main/java/decodex/storage/Storage.java create mode 100644 src/main/java/decodex/ui/Ui.java rename src/test/java/{seedu/duke => decodex}/DukeTest.java (82%) diff --git a/build.gradle b/build.gradle index b0c5528fb5..41a11f95c3 100644 --- a/build.gradle +++ b/build.gradle @@ -29,11 +29,11 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "decodex.Decodex" } shadowJar { - archiveBaseName = "duke" + archiveBaseName = "decodex" archiveClassifier = null } diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/decodex/Decodex.java similarity index 90% rename from src/main/java/seedu/duke/Duke.java rename to src/main/java/decodex/Decodex.java index 16a502c4d6..32658d42df 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/decodex/Decodex.java @@ -1,8 +1,8 @@ -package seedu.duke; +package decodex; import java.util.Scanner; -public class Duke { +public class Decodex { /** * Logo to be changed - possibly to a welcome message instead for Decodex. */ @@ -18,7 +18,7 @@ public class Duke { private static final String EXIT_COMMAND = "exit"; /** - * Main entry-point for the java.duke.Duke application. + * Decodex entry-point for the java.decodex.Decodex application. */ public static void main(String[] args) { System.out.println("Hello from\n" + LOGO); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java new file mode 100644 index 0000000000..def3c222fd --- /dev/null +++ b/src/main/java/decodex/commands/Command.java @@ -0,0 +1,5 @@ +package decodex.commands; + +public class Command { + +} diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java new file mode 100644 index 0000000000..0f64f73745 --- /dev/null +++ b/src/main/java/decodex/data/Data.java @@ -0,0 +1,5 @@ +package decodex.data.exception; + +public class Data { + +} diff --git a/src/main/java/decodex/data/exception/DummyException.java b/src/main/java/decodex/data/exception/DummyException.java new file mode 100644 index 0000000000..a8d2547d81 --- /dev/null +++ b/src/main/java/decodex/data/exception/DummyException.java @@ -0,0 +1,5 @@ +package decodex.data.exception; + +public class DummyException { + +} diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/BaseModule.java new file mode 100644 index 0000000000..ebb59b3a61 --- /dev/null +++ b/src/main/java/decodex/modules/BaseModule.java @@ -0,0 +1,5 @@ +package decodex.modules; + +public class BaseModule { + +} diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java new file mode 100644 index 0000000000..88cdcc52b1 --- /dev/null +++ b/src/main/java/decodex/parser/Parser.java @@ -0,0 +1,5 @@ +package decodex.parser; + +public class Parser { + +} diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java new file mode 100644 index 0000000000..7f1902b054 --- /dev/null +++ b/src/main/java/decodex/storage/Storage.java @@ -0,0 +1,5 @@ +package decodex.storage; + +public class Storage { + +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java new file mode 100644 index 0000000000..84770c2cac --- /dev/null +++ b/src/main/java/decodex/ui/Ui.java @@ -0,0 +1,5 @@ +package decodex.ui; + +public class Ui { + +} diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/decodex/DukeTest.java similarity index 82% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/decodex/DukeTest.java index 2dda5fd651..d6c04368d2 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/decodex/DukeTest.java @@ -1,10 +1,10 @@ -package seedu.duke; +package decodex; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; -class DukeTest { +class DecodexTest { @Test public void sampleTest() { assertTrue(true); From 69904fce723d98cdfbfed196db095aaa1bc50ff2 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 4 Oct 2021 16:57:28 +0800 Subject: [PATCH 014/607] Change name of test file --- src/test/java/decodex/{DukeTest.java => DecodexTest.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/java/decodex/{DukeTest.java => DecodexTest.java} (100%) diff --git a/src/test/java/decodex/DukeTest.java b/src/test/java/decodex/DecodexTest.java similarity index 100% rename from src/test/java/decodex/DukeTest.java rename to src/test/java/decodex/DecodexTest.java From 1ccc560fa132e530c720b20f0d6d200c1b5fa759 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 4 Oct 2021 21:59:48 +0800 Subject: [PATCH 015/607] Add Data Class --- src/main/java/decodex/data/Data.java | 23 +++++++++++++++++- src/test/java/decodex/DecodexTest.java | 1 + src/test/java/decodex/data/DataTest.java | 31 ++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/test/java/decodex/data/DataTest.java diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 0f64f73745..8a178aec6c 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -1,5 +1,26 @@ -package decodex.data.exception; +package decodex.data; +/** + * The Data class manages the data that is to be encoded or decoded. + */ public class Data { + private byte[] rawBytes; + + public Data(byte[] rawBytes) { + this.rawBytes = rawBytes; + } + + public Data(String data) { + rawBytes = data.getBytes(); + } + + public byte[] getRawBytes() { + return rawBytes; + } + + @Override + public String toString() { + return new String(rawBytes); + } } diff --git a/src/test/java/decodex/DecodexTest.java b/src/test/java/decodex/DecodexTest.java index d6c04368d2..4dc5b378c5 100644 --- a/src/test/java/decodex/DecodexTest.java +++ b/src/test/java/decodex/DecodexTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; class DecodexTest { + @Test public void sampleTest() { assertTrue(true); diff --git a/src/test/java/decodex/data/DataTest.java b/src/test/java/decodex/data/DataTest.java new file mode 100644 index 0000000000..e485bbdf43 --- /dev/null +++ b/src/test/java/decodex/data/DataTest.java @@ -0,0 +1,31 @@ +package decodex.data; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +class DataTest { + + @Test + public void getRawBytes_stringConstructor_actualBytesOfString() { + Data testData = new Data("hi"); + byte[] expectedBytes = new byte[]{104, 105}; // ASCII values of "hi" + assertTrue(Arrays.equals(testData.getRawBytes(), expectedBytes)); + } + + @Test + public void getRawBytes_bytesConstructor_actualBytesOfString() { + byte[] expectedBytes = new byte[]{104, 105}; // ASCII values of "hi" + Data testData = new Data(expectedBytes); + assertTrue(Arrays.equals(testData.getRawBytes(), expectedBytes)); + } + + @Test + public void toString_correctStringRepresentation() { + String testString = "hi"; + Data testData = new Data(testString); + assertTrue(testString.equals(testData.toString())); + } +} From da0e8fdcdb7012ee5985e01090608edb962724f5 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 4 Oct 2021 22:27:25 +0800 Subject: [PATCH 016/607] Add abstract BaseModule Class --- src/main/java/decodex/modules/BaseModule.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/BaseModule.java index ebb59b3a61..371273896a 100644 --- a/src/main/java/decodex/modules/BaseModule.java +++ b/src/main/java/decodex/modules/BaseModule.java @@ -1,5 +1,30 @@ package decodex.modules; -public class BaseModule { +import decodex.data.Data; +/** + * BaseModule serves as a foundation for other modules to be built upon. + */ +public abstract class BaseModule { + + protected String name; + protected String description; + + public BaseModule() { + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + /** + * Performs data processing on the provided Data object. Abstract method to be implemented by other modules. + * + * @return Data object containing new processed data. + */ + public abstract Data run(Data data); } From f5dc22d4fea683bd3fc92f9a09586be8c067f47f Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 4 Oct 2021 23:41:35 +0800 Subject: [PATCH 017/607] Add DataManager Class --- src/main/java/decodex/data/DataManager.java | 32 +++++++++++++++++++ .../java/decodex/data/DataManagerTest.java | 30 +++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/main/java/decodex/data/DataManager.java create mode 100644 src/test/java/decodex/data/DataManagerTest.java diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java new file mode 100644 index 0000000000..0c25f65f2e --- /dev/null +++ b/src/main/java/decodex/data/DataManager.java @@ -0,0 +1,32 @@ +package decodex.data; + +/** + * The DataManager class manages the current state of the data. + */ +public class DataManager { + + /* Data provided by the user */ + private final Data originalData; + /* Data to perform the encoding/decoding operations on */ + private Data currentData; + + public DataManager(Data originalData) { + this.originalData = originalData; + currentData = originalData; + } + + public Data getCurrentData() { + return currentData; + } + + public void setCurrentData(Data currentData) { + this.currentData = currentData; + } + + /** + * Undo all modifications done to the data. + */ + public void resetToOriginalData() { + currentData = originalData; + } +} diff --git a/src/test/java/decodex/data/DataManagerTest.java b/src/test/java/decodex/data/DataManagerTest.java new file mode 100644 index 0000000000..e8c366236f --- /dev/null +++ b/src/test/java/decodex/data/DataManagerTest.java @@ -0,0 +1,30 @@ +package decodex.data; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class DataManagerTest { + + @Test + public void resetCurrentData_noUpdates_originalData() { + Data originalData = new Data("hi"); + DataManager testManager = new DataManager(originalData); + + testManager.resetToOriginalData(); + + assertTrue(testManager.getCurrentData() == originalData); + } + + @Test + public void resetCurrentData_updateWithNewData_originalData() { + Data originalData = new Data("hi"); + Data newData = new Data("bye"); + DataManager testManager = new DataManager(originalData); + testManager.setCurrentData(newData); + + testManager.resetToOriginalData(); + + assertTrue(testManager.getCurrentData() == originalData); + } +} From 2fb0f6ac23a7bb596fe5795055b0eb05388761b7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 5 Oct 2021 01:01:11 +0800 Subject: [PATCH 018/607] Replace "==" with Arrays.equals for byte array comparison --- src/test/java/decodex/data/DataManagerTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/data/DataManagerTest.java b/src/test/java/decodex/data/DataManagerTest.java index e8c366236f..ead43a24d4 100644 --- a/src/test/java/decodex/data/DataManagerTest.java +++ b/src/test/java/decodex/data/DataManagerTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; + import org.junit.jupiter.api.Test; class DataManagerTest { @@ -13,7 +15,9 @@ public void resetCurrentData_noUpdates_originalData() { testManager.resetToOriginalData(); - assertTrue(testManager.getCurrentData() == originalData); + Data currentData = testManager.getCurrentData(); + + assertTrue(Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes())); } @Test @@ -25,6 +29,8 @@ public void resetCurrentData_updateWithNewData_originalData() { testManager.resetToOriginalData(); - assertTrue(testManager.getCurrentData() == originalData); + Data currentData = testManager.getCurrentData(); + + assertTrue(Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes())); } } From 9938ab990373e6c7830375567dc02d788bc11bfc Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 5 Oct 2021 01:23:38 +0800 Subject: [PATCH 019/607] Update BaseModule constructor to take in and set name and description --- src/main/java/decodex/modules/BaseModule.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/BaseModule.java index 371273896a..6292f45c33 100644 --- a/src/main/java/decodex/modules/BaseModule.java +++ b/src/main/java/decodex/modules/BaseModule.java @@ -10,7 +10,9 @@ public abstract class BaseModule { protected String name; protected String description; - public BaseModule() { + public BaseModule(String name, String description) { + this.name = name; + this.description = description; } public String getName() { From 5f2cc909f2895690091a340bc732cfc305052ee1 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 5 Oct 2021 19:42:05 +0800 Subject: [PATCH 020/607] Added Base64Encoder and Base64Decoder Classes. Also added JUnit test for the above respective classes. --- .../java/decodex/modules/Base64Decoder.java | 30 +++++++++++++++++++ .../java/decodex/modules/Base64Encoder.java | 30 +++++++++++++++++++ .../decodex/modules/Base64DecoderTest.java | 22 ++++++++++++++ .../decodex/modules/Base64EncoderTest.java | 22 ++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 src/main/java/decodex/modules/Base64Decoder.java create mode 100644 src/main/java/decodex/modules/Base64Encoder.java create mode 100644 src/test/java/decodex/modules/Base64DecoderTest.java create mode 100644 src/test/java/decodex/modules/Base64EncoderTest.java diff --git a/src/main/java/decodex/modules/Base64Decoder.java b/src/main/java/decodex/modules/Base64Decoder.java new file mode 100644 index 0000000000..ff56390163 --- /dev/null +++ b/src/main/java/decodex/modules/Base64Decoder.java @@ -0,0 +1,30 @@ +package decodex.modules; + +import java.util.Base64; + +import decodex.data.Data; + +public class Base64Decoder extends BaseModule { + + private final Base64.Decoder base64Decoder; + + public Base64Decoder(String name, String description) { + super(name, description); + base64Decoder = Base64.getDecoder(); + } + + /** + * Performs Base64 Decoding on the provided Data object. + * + * @param data The Data object of the encoded data. + * @return The Data object of the Base64-decoded data. + */ + @Override + public Data run(Data data) { + Data decodedData; + byte[] originalBytes = data.getRawBytes(); + byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); + decodedData = new Data(base64DecodedBytes); + return decodedData; + } +} diff --git a/src/main/java/decodex/modules/Base64Encoder.java b/src/main/java/decodex/modules/Base64Encoder.java new file mode 100644 index 0000000000..00b4e0a036 --- /dev/null +++ b/src/main/java/decodex/modules/Base64Encoder.java @@ -0,0 +1,30 @@ +package decodex.modules; + +import java.util.Base64; + +import decodex.data.Data; + +public class Base64Encoder extends BaseModule { + + private final Base64.Encoder base64Encoder; + + public Base64Encoder(String name, String description) { + super(name, description); + base64Encoder = Base64.getEncoder(); + } + + /** + * Performs Base64 Encoding on the provided Data object. + * + * @param data The Data object of the original/previous data. + * @return The Data object of the Base64-encoded data. + */ + @Override + public Data run(Data data) { + Data encodedData; + byte[] originalBytes = data.getRawBytes(); + byte[] base64EncodedBytes = base64Encoder.encode(originalBytes); + encodedData = new Data(base64EncodedBytes); + return encodedData; + } +} diff --git a/src/test/java/decodex/modules/Base64DecoderTest.java b/src/test/java/decodex/modules/Base64DecoderTest.java new file mode 100644 index 0000000000..d809f6f0ba --- /dev/null +++ b/src/test/java/decodex/modules/Base64DecoderTest.java @@ -0,0 +1,22 @@ +package decodex.modules; + +import decodex.data.Data; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class Base64DecoderTest { + + private static final String BASE64_DECODER_NAME = "base64decode"; + private static final String BASE64_DECODER_DESC = "Base64-decodes the data"; + + @Test + void run_encodedString_base64DecodedData() { + Base64Decoder base64Decoder = new Base64Decoder(BASE64_DECODER_NAME, BASE64_DECODER_DESC); + String encodedString = "dGVzdA=="; + String expectedString = "test"; + Data encodedData = new Data(encodedString); + Data decodedData = base64Decoder.run(encodedData); + assertEquals(expectedString, decodedData.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/decodex/modules/Base64EncoderTest.java b/src/test/java/decodex/modules/Base64EncoderTest.java new file mode 100644 index 0000000000..7ea9015166 --- /dev/null +++ b/src/test/java/decodex/modules/Base64EncoderTest.java @@ -0,0 +1,22 @@ +package decodex.modules; + +import decodex.data.Data; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class Base64EncoderTest { + + private static final String BASE64_ENCODER_NAME = "base64encode"; + private static final String BASE64_ENCODER_DESC = "Base64-encodes the data"; + + @Test + void run_originalString_base64EncodedData() { + Base64Encoder base64Encoder = new Base64Encoder(BASE64_ENCODER_NAME, BASE64_ENCODER_DESC); + String testString = "test"; + String expectedString = "dGVzdA=="; + Data originalData = new Data(testString); + Data encodedData = base64Encoder.run(originalData); + assertEquals(expectedString, encodedData.toString()); + } +} \ No newline at end of file From 8fb763b7922829d8877a07058db5426b0c751d5d Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 19:43:49 +0800 Subject: [PATCH 021/607] Add Hex Encoder module - Add hexEncoder module - Add JUnitTest for hexEncoder --- .../java/decodex/modules/hex/HexEncode.java | 34 +++++++++++++++++++ .../decodex/modules/hex/HexEncodeTest.java | 26 ++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/decodex/modules/hex/HexEncode.java create mode 100644 src/test/java/decodex/modules/hex/HexEncodeTest.java diff --git a/src/main/java/decodex/modules/hex/HexEncode.java b/src/main/java/decodex/modules/hex/HexEncode.java new file mode 100644 index 0000000000..337d893036 --- /dev/null +++ b/src/main/java/decodex/modules/hex/HexEncode.java @@ -0,0 +1,34 @@ +package decodex.modules.hex; + +import decodex.data.Data; +import decodex.modules.BaseModule; + +public class HexEncode extends BaseModule { + + public static final String MODULE_NAME = "hex-encode"; + + public static final String MODULE_DESCRIPTION = "Converts the input string to hexadecimal bytes"; + public static final String MODULE_EXAMPLE = "\"takoyaki\" becomes \"74 61 6b 6f 79 61 6b 69\""; + + public HexEncode() { + super(MODULE_NAME, MODULE_DESCRIPTION); + + } + + /** + * Encodes the given Data object to Hexadecimal + * @param data Data object to be encoded + * @return new Data object representing the encoded input + */ + @Override + public Data run(Data data) { + StringBuilder encodedStringBuilder = new StringBuilder(); + + for (byte b : data.getRawBytes()) { + encodedStringBuilder.append(String.format("%02X ", b)); + } + + String encodedString = encodedStringBuilder.toString().trim(); + return new Data(encodedString); + } +} diff --git a/src/test/java/decodex/modules/hex/HexEncodeTest.java b/src/test/java/decodex/modules/hex/HexEncodeTest.java new file mode 100644 index 0000000000..0bb6755439 --- /dev/null +++ b/src/test/java/decodex/modules/hex/HexEncodeTest.java @@ -0,0 +1,26 @@ +package decodex.modules.hex; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import decodex.data.Data; +import org.junit.jupiter.api.Test; + +class HexEncodeTest { + + @Test + void run_ascii_expectHexEncoding() { + String inputString = " !\"#$%&'()*+,-./0123456789:;" + + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + HexEncode hexEncode = new HexEncode(); + Data inputData = new Data(inputString); + + Data result = hexEncode.run(inputData); + String expectedString = "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B " + + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" + + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " + + "7D 7E"; + + assertEquals(expectedString, result.toString()); + } + +} \ No newline at end of file From 9997c47e5ef6d282d9e0ca01cb5fda72ff6f01df Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:01:49 +0800 Subject: [PATCH 022/607] Add Hex Decoder module - Add hexDecoder module - Add JUnitTest for hexDecoder --- .../java/decodex/modules/hex/HexDecode.java | 37 +++++++++++++++++++ .../decodex/modules/hex/HexDecodeTest.java | 25 +++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/main/java/decodex/modules/hex/HexDecode.java create mode 100644 src/test/java/decodex/modules/hex/HexDecodeTest.java diff --git a/src/main/java/decodex/modules/hex/HexDecode.java b/src/main/java/decodex/modules/hex/HexDecode.java new file mode 100644 index 0000000000..6e88835175 --- /dev/null +++ b/src/main/java/decodex/modules/hex/HexDecode.java @@ -0,0 +1,37 @@ +package decodex.modules.hex; + +import decodex.data.Data; +import decodex.modules.BaseModule; +import java.util.Arrays; +import java.util.stream.Collectors; + +public class HexDecode extends BaseModule { + + public static final String MODULE_NAME = "hex-decode"; + + public static final String MODULE_DESCRIPTION = "Converts a hexadecimal string back into its raw value"; + public static final String MODULE_EXAMPLE = "\"74 61 6b 6f 79 61 6b 69\" becomes \"takoyaki\""; + + public HexDecode() { + super(MODULE_NAME, MODULE_DESCRIPTION); + + } + + /** + * Decodes the given hexadecimal string back into its raw value + * @param data Data object to be decoded + * @return new Data object representing the decoded input + */ + @Override + public Data run(Data data) { + String inputString = data.toString(); + inputString = inputString.replace(" ", ""); + + String DecodedString = Arrays + .stream(inputString.split("(?<=\\G..)")) + .map(s -> Character.toString((char)Integer.parseInt(s, 16))) + .collect(Collectors.joining()); + + return new Data(DecodedString); + } +} diff --git a/src/test/java/decodex/modules/hex/HexDecodeTest.java b/src/test/java/decodex/modules/hex/HexDecodeTest.java new file mode 100644 index 0000000000..c64e850ef5 --- /dev/null +++ b/src/test/java/decodex/modules/hex/HexDecodeTest.java @@ -0,0 +1,25 @@ +package decodex.modules.hex; + +import static org.junit.jupiter.api.Assertions.*; + +import decodex.data.Data; +import org.junit.jupiter.api.Test; + +class HexDecodeTest { + + @Test + void run_HexEncoding_expectascii() { + String inputString = "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B " + + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" + + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " + + "7D 7E"; + HexDecode hexEncode = new HexDecode(); + Data inputData = new Data(inputString); + + Data result = hexEncode.run(inputData); + String expectedString = " !\"#$%&'()*+,-./0123456789:;" + + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + + assertEquals(expectedString, result.toString()); + } +} \ No newline at end of file From ede4da74e7e775d766132350bde022a25df3931d Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:06:09 +0800 Subject: [PATCH 023/607] Fix style --- src/main/java/decodex/modules/hex/HexEncode.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexEncode.java b/src/main/java/decodex/modules/hex/HexEncode.java index 337d893036..c1e32e2c09 100644 --- a/src/main/java/decodex/modules/hex/HexEncode.java +++ b/src/main/java/decodex/modules/hex/HexEncode.java @@ -16,9 +16,10 @@ public HexEncode() { } /** - * Encodes the given Data object to Hexadecimal - * @param data Data object to be encoded - * @return new Data object representing the encoded input + * Encodes the given Data object to Hexadecimal. + * + * @param data Data object to be encoded. + * @return new Data object representing the encoded input. */ @Override public Data run(Data data) { From a5ff83985b307751773fc6ac79d41331d09a43b5 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:06:54 +0800 Subject: [PATCH 024/607] Fix style --- src/main/java/decodex/modules/hex/HexDecode.java | 9 +++++---- src/test/java/decodex/modules/hex/HexDecodeTest.java | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecode.java b/src/main/java/decodex/modules/hex/HexDecode.java index 6e88835175..512daddf89 100644 --- a/src/main/java/decodex/modules/hex/HexDecode.java +++ b/src/main/java/decodex/modules/hex/HexDecode.java @@ -18,9 +18,10 @@ public HexDecode() { } /** - * Decodes the given hexadecimal string back into its raw value - * @param data Data object to be decoded - * @return new Data object representing the decoded input + * Decodes the given hexadecimal string back into its raw value. + * + * @param data Data object to be decoded. + * @return new Data object representing the decoded input. */ @Override public Data run(Data data) { @@ -29,7 +30,7 @@ public Data run(Data data) { String DecodedString = Arrays .stream(inputString.split("(?<=\\G..)")) - .map(s -> Character.toString((char)Integer.parseInt(s, 16))) + .map(s -> Character.toString((char) Integer.parseInt(s, 16))) .collect(Collectors.joining()); return new Data(DecodedString); diff --git a/src/test/java/decodex/modules/hex/HexDecodeTest.java b/src/test/java/decodex/modules/hex/HexDecodeTest.java index c64e850ef5..56c2066f0a 100644 --- a/src/test/java/decodex/modules/hex/HexDecodeTest.java +++ b/src/test/java/decodex/modules/hex/HexDecodeTest.java @@ -1,6 +1,6 @@ package decodex.modules.hex; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import decodex.data.Data; import org.junit.jupiter.api.Test; @@ -13,10 +13,10 @@ void run_HexEncoding_expectascii() { + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " + "7D 7E"; - HexDecode hexEncode = new HexDecode(); + HexDecode HexDecode = new HexDecode(); Data inputData = new Data(inputString); - Data result = hexEncode.run(inputData); + Data result = HexDecode.run(inputData); String expectedString = " !\"#$%&'()*+,-./0123456789:;" + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; From b635dfd0b641dc353ad500e772f38222147f8a5d Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:10:18 +0800 Subject: [PATCH 025/607] Fix style --- src/main/java/decodex/modules/hex/HexDecode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecode.java b/src/main/java/decodex/modules/hex/HexDecode.java index 512daddf89..383f8d771d 100644 --- a/src/main/java/decodex/modules/hex/HexDecode.java +++ b/src/main/java/decodex/modules/hex/HexDecode.java @@ -28,11 +28,11 @@ public Data run(Data data) { String inputString = data.toString(); inputString = inputString.replace(" ", ""); - String DecodedString = Arrays + String decodedString = Arrays .stream(inputString.split("(?<=\\G..)")) .map(s -> Character.toString((char) Integer.parseInt(s, 16))) .collect(Collectors.joining()); - return new Data(DecodedString); + return new Data(decodedString); } } From 027637ae67edaeecdccbd85eb1673a8b8cd55a88 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:15:42 +0800 Subject: [PATCH 026/607] Fix style --- src/test/java/decodex/modules/hex/HexDecodeTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/modules/hex/HexDecodeTest.java b/src/test/java/decodex/modules/hex/HexDecodeTest.java index 56c2066f0a..b26b5a4a4c 100644 --- a/src/test/java/decodex/modules/hex/HexDecodeTest.java +++ b/src/test/java/decodex/modules/hex/HexDecodeTest.java @@ -13,10 +13,10 @@ void run_HexEncoding_expectascii() { + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " + "7D 7E"; - HexDecode HexDecode = new HexDecode(); + HexDecode hexDecode = new HexDecode(); Data inputData = new Data(inputString); - Data result = HexDecode.run(inputData); + Data result = hexDecode.run(inputData); String expectedString = " !\"#$%&'()*+,-./0123456789:;" + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; From 4a0e1c2b8a487835d0407400cb314bf72cc50756 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:18:23 +0800 Subject: [PATCH 027/607] Rename module file HexDecode -> HexDecoder --- .../decodex/modules/hex/{HexDecode.java => HexDecoder.java} | 4 ++-- .../modules/hex/{HexDecodeTest.java => HexDecoderTest.java} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/decodex/modules/hex/{HexDecode.java => HexDecoder.java} (93%) rename src/test/java/decodex/modules/hex/{HexDecodeTest.java => HexDecoderTest.java} (87%) diff --git a/src/main/java/decodex/modules/hex/HexDecode.java b/src/main/java/decodex/modules/hex/HexDecoder.java similarity index 93% rename from src/main/java/decodex/modules/hex/HexDecode.java rename to src/main/java/decodex/modules/hex/HexDecoder.java index 383f8d771d..429e4f2318 100644 --- a/src/main/java/decodex/modules/hex/HexDecode.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -5,14 +5,14 @@ import java.util.Arrays; import java.util.stream.Collectors; -public class HexDecode extends BaseModule { +public class HexDecoder extends BaseModule { public static final String MODULE_NAME = "hex-decode"; public static final String MODULE_DESCRIPTION = "Converts a hexadecimal string back into its raw value"; public static final String MODULE_EXAMPLE = "\"74 61 6b 6f 79 61 6b 69\" becomes \"takoyaki\""; - public HexDecode() { + public HexDecoder() { super(MODULE_NAME, MODULE_DESCRIPTION); } diff --git a/src/test/java/decodex/modules/hex/HexDecodeTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java similarity index 87% rename from src/test/java/decodex/modules/hex/HexDecodeTest.java rename to src/test/java/decodex/modules/hex/HexDecoderTest.java index b26b5a4a4c..6eb0ffd80b 100644 --- a/src/test/java/decodex/modules/hex/HexDecodeTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -5,7 +5,7 @@ import decodex.data.Data; import org.junit.jupiter.api.Test; -class HexDecodeTest { +class HexDecoderTest { @Test void run_HexEncoding_expectascii() { @@ -13,10 +13,10 @@ void run_HexEncoding_expectascii() { + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " + "7D 7E"; - HexDecode hexDecode = new HexDecode(); + HexDecoder hexDecoder = new HexDecoder(); Data inputData = new Data(inputString); - Data result = hexDecode.run(inputData); + Data result = hexDecoder.run(inputData); String expectedString = " !\"#$%&'()*+,-./0123456789:;" + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; From 015bac389804888c449f401fd15f3cdbfefa37e0 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:19:06 +0800 Subject: [PATCH 028/607] Rename module file HexEncode -> HexEncoder --- .../decodex/modules/hex/{HexEncode.java => HexEncoder.java} | 4 ++-- .../modules/hex/{HexEncodeTest.java => HexEncoderTest.java} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/decodex/modules/hex/{HexEncode.java => HexEncoder.java} (93%) rename src/test/java/decodex/modules/hex/{HexEncodeTest.java => HexEncoderTest.java} (87%) diff --git a/src/main/java/decodex/modules/hex/HexEncode.java b/src/main/java/decodex/modules/hex/HexEncoder.java similarity index 93% rename from src/main/java/decodex/modules/hex/HexEncode.java rename to src/main/java/decodex/modules/hex/HexEncoder.java index c1e32e2c09..f80dd7b17e 100644 --- a/src/main/java/decodex/modules/hex/HexEncode.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -3,14 +3,14 @@ import decodex.data.Data; import decodex.modules.BaseModule; -public class HexEncode extends BaseModule { +public class HexEncoder extends BaseModule { public static final String MODULE_NAME = "hex-encode"; public static final String MODULE_DESCRIPTION = "Converts the input string to hexadecimal bytes"; public static final String MODULE_EXAMPLE = "\"takoyaki\" becomes \"74 61 6b 6f 79 61 6b 69\""; - public HexEncode() { + public HexEncoder() { super(MODULE_NAME, MODULE_DESCRIPTION); } diff --git a/src/test/java/decodex/modules/hex/HexEncodeTest.java b/src/test/java/decodex/modules/hex/HexEncoderTest.java similarity index 87% rename from src/test/java/decodex/modules/hex/HexEncodeTest.java rename to src/test/java/decodex/modules/hex/HexEncoderTest.java index 0bb6755439..19ddc788ef 100644 --- a/src/test/java/decodex/modules/hex/HexEncodeTest.java +++ b/src/test/java/decodex/modules/hex/HexEncoderTest.java @@ -5,16 +5,16 @@ import decodex.data.Data; import org.junit.jupiter.api.Test; -class HexEncodeTest { +class HexEncoderTest { @Test void run_ascii_expectHexEncoding() { String inputString = " !\"#$%&'()*+,-./0123456789:;" + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; - HexEncode hexEncode = new HexEncode(); + HexEncoder hexEncoder = new HexEncoder(); Data inputData = new Data(inputString); - Data result = hexEncode.run(inputData); + Data result = hexEncoder.run(inputData); String expectedString = "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B " + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " From ea792273882a714e43b6b5e7e908e89a4ff17537 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:29:16 +0800 Subject: [PATCH 029/607] Remove dead code --- src/main/java/decodex/modules/hex/HexDecoder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 429e4f2318..4f51706d63 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -10,7 +10,6 @@ public class HexDecoder extends BaseModule { public static final String MODULE_NAME = "hex-decode"; public static final String MODULE_DESCRIPTION = "Converts a hexadecimal string back into its raw value"; - public static final String MODULE_EXAMPLE = "\"74 61 6b 6f 79 61 6b 69\" becomes \"takoyaki\""; public HexDecoder() { super(MODULE_NAME, MODULE_DESCRIPTION); From 6997784733900e5fcfc4d8c5952f9220990f3dd7 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 20:29:33 +0800 Subject: [PATCH 030/607] Remove dead code --- src/main/java/decodex/modules/hex/HexEncoder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index f80dd7b17e..fe660024c3 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -8,7 +8,6 @@ public class HexEncoder extends BaseModule { public static final String MODULE_NAME = "hex-encode"; public static final String MODULE_DESCRIPTION = "Converts the input string to hexadecimal bytes"; - public static final String MODULE_EXAMPLE = "\"takoyaki\" becomes \"74 61 6b 6f 79 61 6b 69\""; public HexEncoder() { super(MODULE_NAME, MODULE_DESCRIPTION); From 7d06d6bd241cfb70aaba24fc9018e9799b4d2e3c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 5 Oct 2021 20:55:01 +0800 Subject: [PATCH 031/607] Changes to Base64Decoder and Encoder's Class constructor. Also made some changes to the class by adding public constants for the name and description of the respective modules. --- src/main/java/decodex/modules/Base64Decoder.java | 7 +++++-- src/main/java/decodex/modules/Base64Encoder.java | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/modules/Base64Decoder.java b/src/main/java/decodex/modules/Base64Decoder.java index ff56390163..9a51781cc9 100644 --- a/src/main/java/decodex/modules/Base64Decoder.java +++ b/src/main/java/decodex/modules/Base64Decoder.java @@ -8,8 +8,11 @@ public class Base64Decoder extends BaseModule { private final Base64.Decoder base64Decoder; - public Base64Decoder(String name, String description) { - super(name, description); + public static final String MODULE_NAME = "base64decode"; + public static final String MODULE_DESCRIPTION = "Decodes the data using Base64 format."; + + public Base64Decoder() { + super(MODULE_NAME, MODULE_DESCRIPTION); base64Decoder = Base64.getDecoder(); } diff --git a/src/main/java/decodex/modules/Base64Encoder.java b/src/main/java/decodex/modules/Base64Encoder.java index 00b4e0a036..aa6fe1d9b9 100644 --- a/src/main/java/decodex/modules/Base64Encoder.java +++ b/src/main/java/decodex/modules/Base64Encoder.java @@ -8,8 +8,11 @@ public class Base64Encoder extends BaseModule { private final Base64.Encoder base64Encoder; - public Base64Encoder(String name, String description) { - super(name, description); + public static final String MODULE_NAME = "base64encode"; + public static final String MODULE_DESCRIPTION = "Base64-encodes the data"; + + public Base64Encoder() { + super(MODULE_NAME, MODULE_DESCRIPTION); base64Encoder = Base64.getEncoder(); } From 554766712e7b5d771fbfc371d83c56c4e3a943f9 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 5 Oct 2021 20:55:33 +0800 Subject: [PATCH 032/607] Updated the JUnit test with respect to the changes to the Base64 Module classes. --- src/test/java/decodex/modules/Base64DecoderTest.java | 6 ++---- src/test/java/decodex/modules/Base64EncoderTest.java | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/test/java/decodex/modules/Base64DecoderTest.java b/src/test/java/decodex/modules/Base64DecoderTest.java index d809f6f0ba..1f68bd27e9 100644 --- a/src/test/java/decodex/modules/Base64DecoderTest.java +++ b/src/test/java/decodex/modules/Base64DecoderTest.java @@ -7,14 +7,12 @@ class Base64DecoderTest { - private static final String BASE64_DECODER_NAME = "base64decode"; - private static final String BASE64_DECODER_DESC = "Base64-decodes the data"; - @Test void run_encodedString_base64DecodedData() { - Base64Decoder base64Decoder = new Base64Decoder(BASE64_DECODER_NAME, BASE64_DECODER_DESC); + Base64Decoder base64Decoder = new Base64Decoder(); String encodedString = "dGVzdA=="; String expectedString = "test"; + Data encodedData = new Data(encodedString); Data decodedData = base64Decoder.run(encodedData); assertEquals(expectedString, decodedData.toString()); diff --git a/src/test/java/decodex/modules/Base64EncoderTest.java b/src/test/java/decodex/modules/Base64EncoderTest.java index 7ea9015166..1be59d0b44 100644 --- a/src/test/java/decodex/modules/Base64EncoderTest.java +++ b/src/test/java/decodex/modules/Base64EncoderTest.java @@ -7,14 +7,12 @@ class Base64EncoderTest { - private static final String BASE64_ENCODER_NAME = "base64encode"; - private static final String BASE64_ENCODER_DESC = "Base64-encodes the data"; - @Test void run_originalString_base64EncodedData() { - Base64Encoder base64Encoder = new Base64Encoder(BASE64_ENCODER_NAME, BASE64_ENCODER_DESC); + Base64Encoder base64Encoder = new Base64Encoder(); String testString = "test"; String expectedString = "dGVzdA=="; + Data originalData = new Data(testString); Data encodedData = base64Encoder.run(originalData); assertEquals(expectedString, encodedData.toString()); From 91c48dedede795a1759e38be02a308019ee5e636 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 21:17:27 +0800 Subject: [PATCH 033/607] Add abstract command class and ExitCommand - Add abstract command class - Add Exit Command - Add temporary code to allow running of program --- src/main/java/decodex/Decodex.java | 36 ++++++++++++++----- src/main/java/decodex/commands/Command.java | 18 +++++++++- .../java/decodex/commands/ExitCommand.java | 11 ++++++ src/main/java/decodex/data/DataManager.java | 5 +++ .../java/decodex/modules/ModuleManager.java | 5 +++ 5 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 src/main/java/decodex/commands/ExitCommand.java create mode 100644 src/main/java/decodex/data/DataManager.java create mode 100644 src/main/java/decodex/modules/ModuleManager.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 32658d42df..5202844172 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -1,8 +1,14 @@ package decodex; +import decodex.commands.Command; +import decodex.commands.ExitCommand; +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; import java.util.Scanner; public class Decodex { + /** * Logo to be changed - possibly to a welcome message instead for Decodex. */ @@ -12,31 +18,43 @@ public class Decodex { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; - /** - * Command Prefix Constants. - */ - private static final String EXIT_COMMAND = "exit"; - /** * Decodex entry-point for the java.decodex.Decodex application. */ public static void main(String[] args) { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + System.out.println("Hello from\n" + LOGO); Scanner in = new Scanner(System.in); - while (true) { + + boolean isExit = false; + + while (!isExit) { System.out.print("Decodex > "); String userInput = in.nextLine(); + // Temporary code, command functions will be moved to parser + Command command = null; + switch (userInput) { - case EXIT_COMMAND: - System.out.print("Goodbye!"); - System.exit(0); + case ExitCommand.COMMAND_WORD: + command = new ExitCommand(); break; default: // Skeletal - Just "echos" back to us. System.out.println(userInput); } + + if (command != null){ + command.run(dataManager, moduleManager, ui); + isExit = command.isExit(); + } } + + System.out.print("Goodbye!"); + System.exit(0); } } diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index def3c222fd..f61fa1eeaa 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -1,5 +1,21 @@ package decodex.commands; -public class Command { +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; +import java.util.HashMap; + +/** + * Command serves as a foundation for other commands to be built upon. + */ +public abstract class Command { + + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + + } + + public boolean isExit() { + return false; + } } diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java new file mode 100644 index 0000000000..90ff53433c --- /dev/null +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -0,0 +1,11 @@ +package decodex.commands; + +public class ExitCommand extends Command { + + public static final String COMMAND_WORD = "exit"; + + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java new file mode 100644 index 0000000000..bf10b89c63 --- /dev/null +++ b/src/main/java/decodex/data/DataManager.java @@ -0,0 +1,5 @@ +package decodex.data; + +public class DataManager { + // DUMMY DATAMANAGER FILE, TO BE REPLACED +} diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java new file mode 100644 index 0000000000..d3815b1a44 --- /dev/null +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -0,0 +1,5 @@ +package decodex.modules; + +public class ModuleManager { + // DUMMY MODULEMANAGER FILE, TO BE REPLACED +} From 2a0e936e74a14e6662a89e7934abd89be3958988 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 5 Oct 2021 21:22:06 +0800 Subject: [PATCH 034/607] Fix Style --- src/main/java/decodex/Decodex.java | 2 +- src/main/java/decodex/commands/Command.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 5202844172..d55ed41c5a 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -48,7 +48,7 @@ public static void main(String[] args) { System.out.println(userInput); } - if (command != null){ + if (command != null) { command.run(dataManager, moduleManager, ui); isExit = command.isExit(); } diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index f61fa1eeaa..46b91fe271 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -3,7 +3,6 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.ui.Ui; -import java.util.HashMap; /** * Command serves as a foundation for other commands to be built upon. From f252a95d36787f8eb548668d939b40d9215b8ac6 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 5 Oct 2021 21:24:07 +0800 Subject: [PATCH 035/607] Added comments to describe the classes and its purpose. --- src/main/java/decodex/modules/Base64Decoder.java | 3 +++ src/main/java/decodex/modules/Base64Encoder.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/decodex/modules/Base64Decoder.java b/src/main/java/decodex/modules/Base64Decoder.java index 9a51781cc9..8ec1164d15 100644 --- a/src/main/java/decodex/modules/Base64Decoder.java +++ b/src/main/java/decodex/modules/Base64Decoder.java @@ -4,6 +4,9 @@ import decodex.data.Data; +/** + * Base64Encoder serves to carry out the Base64 decoding operations. + */ public class Base64Decoder extends BaseModule { private final Base64.Decoder base64Decoder; diff --git a/src/main/java/decodex/modules/Base64Encoder.java b/src/main/java/decodex/modules/Base64Encoder.java index aa6fe1d9b9..016ec412c0 100644 --- a/src/main/java/decodex/modules/Base64Encoder.java +++ b/src/main/java/decodex/modules/Base64Encoder.java @@ -4,6 +4,9 @@ import decodex.data.Data; +/** + * Base64Encoder serves to carry out the Base64 encoding operations. + */ public class Base64Encoder extends BaseModule { private final Base64.Encoder base64Encoder; From 3233b899d0559a5d2d9f01578a02c4b9de751aef Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 5 Oct 2021 21:37:57 +0800 Subject: [PATCH 036/607] Add requested newline --- src/main/java/decodex/data/DataManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index 0c25f65f2e..afe4ba7b6e 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -7,6 +7,7 @@ public class DataManager { /* Data provided by the user */ private final Data originalData; + /* Data to perform the encoding/decoding operations on */ private Data currentData; From 80d6c0534ee82bc1c3f2c82f490938a846333952 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 5 Oct 2021 21:55:06 +0800 Subject: [PATCH 037/607] Changes to newlines in end of file. --- src/main/java/decodex/modules/Base64Decoder.java | 2 +- src/main/java/decodex/modules/Base64Encoder.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/Base64Decoder.java b/src/main/java/decodex/modules/Base64Decoder.java index 8ec1164d15..1b39284205 100644 --- a/src/main/java/decodex/modules/Base64Decoder.java +++ b/src/main/java/decodex/modules/Base64Decoder.java @@ -33,4 +33,4 @@ public Data run(Data data) { decodedData = new Data(base64DecodedBytes); return decodedData; } -} +} \ No newline at end of file diff --git a/src/main/java/decodex/modules/Base64Encoder.java b/src/main/java/decodex/modules/Base64Encoder.java index 016ec412c0..f74bc7c838 100644 --- a/src/main/java/decodex/modules/Base64Encoder.java +++ b/src/main/java/decodex/modules/Base64Encoder.java @@ -33,4 +33,4 @@ public Data run(Data data) { encodedData = new Data(base64EncodedBytes); return encodedData; } -} +} \ No newline at end of file From 464e8a927c89c0f88f7903db6b4673d6ba39c4dc Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 5 Oct 2021 22:13:14 +0800 Subject: [PATCH 038/607] Another update to the newlines. --- src/main/java/decodex/modules/Base64Decoder.java | 2 +- src/main/java/decodex/modules/Base64Encoder.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/Base64Decoder.java b/src/main/java/decodex/modules/Base64Decoder.java index 1b39284205..8ec1164d15 100644 --- a/src/main/java/decodex/modules/Base64Decoder.java +++ b/src/main/java/decodex/modules/Base64Decoder.java @@ -33,4 +33,4 @@ public Data run(Data data) { decodedData = new Data(base64DecodedBytes); return decodedData; } -} \ No newline at end of file +} diff --git a/src/main/java/decodex/modules/Base64Encoder.java b/src/main/java/decodex/modules/Base64Encoder.java index f74bc7c838..016ec412c0 100644 --- a/src/main/java/decodex/modules/Base64Encoder.java +++ b/src/main/java/decodex/modules/Base64Encoder.java @@ -33,4 +33,4 @@ public Data run(Data data) { encodedData = new Data(base64EncodedBytes); return encodedData; } -} \ No newline at end of file +} From b218967a9c507fecf93908a4c4e27881180fab7c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 6 Oct 2021 00:13:50 +0800 Subject: [PATCH 039/607] Should be the last commit for the newlines for end of file. --- src/test/java/decodex/modules/Base64DecoderTest.java | 2 +- src/test/java/decodex/modules/Base64EncoderTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/modules/Base64DecoderTest.java b/src/test/java/decodex/modules/Base64DecoderTest.java index 1f68bd27e9..5d470a354e 100644 --- a/src/test/java/decodex/modules/Base64DecoderTest.java +++ b/src/test/java/decodex/modules/Base64DecoderTest.java @@ -17,4 +17,4 @@ void run_encodedString_base64DecodedData() { Data decodedData = base64Decoder.run(encodedData); assertEquals(expectedString, decodedData.toString()); } -} \ No newline at end of file +} diff --git a/src/test/java/decodex/modules/Base64EncoderTest.java b/src/test/java/decodex/modules/Base64EncoderTest.java index 1be59d0b44..994b0721bc 100644 --- a/src/test/java/decodex/modules/Base64EncoderTest.java +++ b/src/test/java/decodex/modules/Base64EncoderTest.java @@ -17,4 +17,4 @@ void run_originalString_base64EncodedData() { Data encodedData = base64Encoder.run(originalData); assertEquals(expectedString, encodedData.toString()); } -} \ No newline at end of file +} From e700ed1eeb5e116a47f38a90280f689b010420dd Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 6 Oct 2021 12:52:02 +0800 Subject: [PATCH 040/607] Added some of the "base level" methods for parser class. 1. getCommandType 2. getUserArgument (Returns a single string argument since our first v1.0 only has one single parameter) --- src/main/java/decodex/parser/Parser.java | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 88cdcc52b1..63977ee814 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -1,5 +1,36 @@ package decodex.parser; +/** + * Parses and validates the user input. + */ public class Parser { + /** + * Returns the type of command that user has entered. + * + * @param userInput The input specified by the user. + * @return The type of command. + */ + public String getCommandType(String userInput) { + String[] tokens = userInput.split(" "); + String commandType = tokens[0]; + return commandType; + } + + /** + * Returns the Argument portion of the user input. + * + * @param userInput The input specified by the user. + * @return The argument portion of the user input. + */ + public String getUserArgument(String userInput) { + try { + String[] tokens = userInput.split(" "); + String userArgument = tokens[1]; + return userArgument; + } catch (ArrayIndexOutOfBoundsException err) { + // Temporary error handling + return ""; + } + } } From 320ad19df306a9f2a906d214adb9fd37a3ee5a69 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 6 Oct 2021 12:52:53 +0800 Subject: [PATCH 041/607] Added JUnit testing for Parser. --- src/test/java/decodex/parser/ParserTest.java | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/test/java/decodex/parser/ParserTest.java diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java new file mode 100644 index 0000000000..1236fbe9ce --- /dev/null +++ b/src/test/java/decodex/parser/ParserTest.java @@ -0,0 +1,38 @@ +package decodex.parser; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ParserTest { + + @Test + void getCommandType_stringWithNoSeparator_expectCommandTypeString() { + String testInput = "test"; + String expectedOutput = "test"; + Parser parser = new Parser(); + + String commandType = parser.getCommandType(testInput); + assertEquals(expectedOutput, commandType); + } + + @Test + void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() { + String testInput = "test data"; + String expectedOutput = "test"; + Parser parser = new Parser(); + + String commandType = parser.getCommandType(testInput); + assertEquals(expectedOutput, commandType); + } + + @Test + void getUserArgument_userInputWithOneSpaceSeparator_expectArgumentString() { + String testInput = "test data"; + String expectedOutput = "data"; + Parser parser = new Parser(); + + String argumentString = parser.getUserArgument(testInput); + assertEquals(expectedOutput, argumentString); + } +} \ No newline at end of file From 87175691d8c4c2db4241ba1f954b72aa0d5b6e27 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 6 Oct 2021 13:00:38 +0800 Subject: [PATCH 042/607] Made some changes to Decodex.java to incorporate the Parser's methods. --- src/main/java/decodex/Decodex.java | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 32658d42df..715d141f41 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -1,8 +1,16 @@ package decodex; +import decodex.parser.Parser; + import java.util.Scanner; public class Decodex { + + /** + * Necessary objects to be initialized for Decodex to work properly. + */ + private static Parser parser; + /** * Logo to be changed - possibly to a welcome message instead for Decodex. */ @@ -17,18 +25,28 @@ public class Decodex { */ private static final String EXIT_COMMAND = "exit"; + /** + * Initializes the necessary Objects for Decodex. + */ + public static void initDecodex() { + parser = new Parser(); + } + /** * Decodex entry-point for the java.decodex.Decodex application. */ public static void main(String[] args) { System.out.println("Hello from\n" + LOGO); + initDecodex(); Scanner in = new Scanner(System.in); while (true) { System.out.print("Decodex > "); String userInput = in.nextLine(); + String command = parser.getCommandType(userInput); + String userArgument = parser.getUserArgument(userInput); - switch (userInput) { + switch (command) { case EXIT_COMMAND: System.out.print("Goodbye!"); System.exit(0); From 8dece0b3984311e22cf35f818db76a1aad92cab9 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 6 Oct 2021 16:07:48 +0800 Subject: [PATCH 043/607] Resolve suggested changes --- src/main/java/decodex/modules/hex/HexEncoder.java | 8 +++++--- src/test/java/decodex/modules/hex/HexEncoderTest.java | 9 ++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index fe660024c3..5c69ad18f9 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -3,10 +3,12 @@ import decodex.data.Data; import decodex.modules.BaseModule; +/** + * The HexEncoder class converts the input string to hexadecimal bytes. + */ public class HexEncoder extends BaseModule { - public static final String MODULE_NAME = "hex-encode"; - + public static final String MODULE_NAME = "hexencode"; public static final String MODULE_DESCRIPTION = "Converts the input string to hexadecimal bytes"; public HexEncoder() { @@ -25,7 +27,7 @@ public Data run(Data data) { StringBuilder encodedStringBuilder = new StringBuilder(); for (byte b : data.getRawBytes()) { - encodedStringBuilder.append(String.format("%02X ", b)); + encodedStringBuilder.append(String.format("%02x", b)); } String encodedString = encodedStringBuilder.toString().trim(); diff --git a/src/test/java/decodex/modules/hex/HexEncoderTest.java b/src/test/java/decodex/modules/hex/HexEncoderTest.java index 19ddc788ef..b1587f6dc0 100644 --- a/src/test/java/decodex/modules/hex/HexEncoderTest.java +++ b/src/test/java/decodex/modules/hex/HexEncoderTest.java @@ -8,17 +8,16 @@ class HexEncoderTest { @Test - void run_ascii_expectHexEncoding() { + void run_allAsciiChars_expectHexEncoding() { String inputString = " !\"#$%&'()*+,-./0123456789:;" + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; HexEncoder hexEncoder = new HexEncoder(); Data inputData = new Data(inputString); Data result = hexEncoder.run(inputData); - String expectedString = "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B " - + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" - + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " - + "7D 7E"; + String expectedString = "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40" + + "4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768" + + "696a6b6c6d6e6f707172737475767778797a7b7c7d7e"; assertEquals(expectedString, result.toString()); } From 957e4652452ad9b237aaabd5e6c0e4b51e196441 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 6 Oct 2021 16:14:53 +0800 Subject: [PATCH 044/607] Resolve suggested changes --- src/main/java/decodex/modules/hex/HexDecoder.java | 13 +++++++++---- .../java/decodex/modules/hex/HexDecoderTest.java | 9 ++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 4f51706d63..2917a4428e 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -5,12 +5,17 @@ import java.util.Arrays; import java.util.stream.Collectors; +/** + * The HexDecoder class converts a hexadecimal string back into its raw value. + */ public class HexDecoder extends BaseModule { - public static final String MODULE_NAME = "hex-decode"; - + public static final String MODULE_NAME = "hexdecode"; public static final String MODULE_DESCRIPTION = "Converts a hexadecimal string back into its raw value"; + public static final int HEXADECIMAL_RADIX = 16; + public static final String REGEX_SPLIT_EVERY_2_CHARS = "(?<=\\G..)"; + public HexDecoder() { super(MODULE_NAME, MODULE_DESCRIPTION); @@ -28,8 +33,8 @@ public Data run(Data data) { inputString = inputString.replace(" ", ""); String decodedString = Arrays - .stream(inputString.split("(?<=\\G..)")) - .map(s -> Character.toString((char) Integer.parseInt(s, 16))) + .stream(inputString.split(REGEX_SPLIT_EVERY_2_CHARS)) + .map(s -> Character.toString((char) Integer.parseInt(s, HEXADECIMAL_RADIX))) .collect(Collectors.joining()); return new Data(decodedString); diff --git a/src/test/java/decodex/modules/hex/HexDecoderTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java index 6eb0ffd80b..a34b299435 100644 --- a/src/test/java/decodex/modules/hex/HexDecoderTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -8,11 +8,10 @@ class HexDecoderTest { @Test - void run_HexEncoding_expectascii() { - String inputString = "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B " - + "3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C" - + " 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C " - + "7D 7E"; + void run_HexEncoding_expectAllAscii() { + String inputString = "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40" + + "4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768" + + "696a6b6c6d6e6f707172737475767778797a7b7c7d7e"; HexDecoder hexDecoder = new HexDecoder(); Data inputData = new Data(inputString); From c57887ad27ed3b2011e6a55bbdd3de1dfb6c0f50 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 6 Oct 2021 16:21:51 +0800 Subject: [PATCH 045/607] Resolve suggested changes --- src/main/java/decodex/Decodex.java | 17 +++++++---------- src/main/java/decodex/commands/Command.java | 4 ---- src/main/java/decodex/commands/ExitCommand.java | 5 ----- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index d55ed41c5a..68480925b2 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -30,15 +30,13 @@ public static void main(String[] args) { Scanner in = new Scanner(System.in); - boolean isExit = false; + // Temporary code, command functions will be moved to parser + Command command = null; - while (!isExit) { + do { System.out.print("Decodex > "); String userInput = in.nextLine(); - // Temporary code, command functions will be moved to parser - Command command = null; - switch (userInput) { case ExitCommand.COMMAND_WORD: command = new ExitCommand(); @@ -46,13 +44,12 @@ public static void main(String[] args) { default: // Skeletal - Just "echos" back to us. System.out.println(userInput); + continue; } - if (command != null) { - command.run(dataManager, moduleManager, ui); - isExit = command.isExit(); - } - } + command.run(dataManager, moduleManager, ui); + + } while (!(command instanceof ExitCommand)); System.out.print("Goodbye!"); System.exit(0); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index 46b91fe271..ac331c9498 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -13,8 +13,4 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { } - public boolean isExit() { - return false; - } - } diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java index 90ff53433c..37a0039a15 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -3,9 +3,4 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - - @Override - public boolean isExit() { - return true; - } } From 69272ec27f2d4ed681137740d385f93eb759a179 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 6 Oct 2021 16:31:28 +0800 Subject: [PATCH 046/607] Move base64 classes into a base64 folder --- src/main/java/decodex/modules/{ => base64}/Base64Decoder.java | 3 ++- src/main/java/decodex/modules/{ => base64}/Base64Encoder.java | 3 ++- .../java/decodex/modules/{ => base64}/Base64DecoderTest.java | 2 +- .../java/decodex/modules/{ => base64}/Base64EncoderTest.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) rename src/main/java/decodex/modules/{ => base64}/Base64Decoder.java (93%) rename src/main/java/decodex/modules/{ => base64}/Base64Encoder.java (93%) rename src/test/java/decodex/modules/{ => base64}/Base64DecoderTest.java (94%) rename src/test/java/decodex/modules/{ => base64}/Base64EncoderTest.java (94%) diff --git a/src/main/java/decodex/modules/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java similarity index 93% rename from src/main/java/decodex/modules/Base64Decoder.java rename to src/main/java/decodex/modules/base64/Base64Decoder.java index 8ec1164d15..5733578325 100644 --- a/src/main/java/decodex/modules/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -1,5 +1,6 @@ -package decodex.modules; +package decodex.modules.base64; +import decodex.modules.BaseModule; import java.util.Base64; import decodex.data.Data; diff --git a/src/main/java/decodex/modules/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java similarity index 93% rename from src/main/java/decodex/modules/Base64Encoder.java rename to src/main/java/decodex/modules/base64/Base64Encoder.java index 016ec412c0..19e520d771 100644 --- a/src/main/java/decodex/modules/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -1,5 +1,6 @@ -package decodex.modules; +package decodex.modules.base64; +import decodex.modules.BaseModule; import java.util.Base64; import decodex.data.Data; diff --git a/src/test/java/decodex/modules/Base64DecoderTest.java b/src/test/java/decodex/modules/base64/Base64DecoderTest.java similarity index 94% rename from src/test/java/decodex/modules/Base64DecoderTest.java rename to src/test/java/decodex/modules/base64/Base64DecoderTest.java index 5d470a354e..68954fecf3 100644 --- a/src/test/java/decodex/modules/Base64DecoderTest.java +++ b/src/test/java/decodex/modules/base64/Base64DecoderTest.java @@ -1,4 +1,4 @@ -package decodex.modules; +package decodex.modules.base64; import decodex.data.Data; import org.junit.jupiter.api.Test; diff --git a/src/test/java/decodex/modules/Base64EncoderTest.java b/src/test/java/decodex/modules/base64/Base64EncoderTest.java similarity index 94% rename from src/test/java/decodex/modules/Base64EncoderTest.java rename to src/test/java/decodex/modules/base64/Base64EncoderTest.java index 994b0721bc..d8fdc62f32 100644 --- a/src/test/java/decodex/modules/Base64EncoderTest.java +++ b/src/test/java/decodex/modules/base64/Base64EncoderTest.java @@ -1,4 +1,4 @@ -package decodex.modules; +package decodex.modules.base64; import decodex.data.Data; import org.junit.jupiter.api.Test; From 128b50e0b47ac1907019a022c417e80e51fb91c0 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 6 Oct 2021 16:31:57 +0800 Subject: [PATCH 047/607] Resolve suggested changes - Made Command more abstract --- src/main/java/decodex/Decodex.java | 5 ++--- src/main/java/decodex/commands/Command.java | 10 +++++++++- src/main/java/decodex/commands/ExitCommand.java | 13 +++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 68480925b2..44e9f16a6b 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -39,7 +39,7 @@ public static void main(String[] args) { switch (userInput) { case ExitCommand.COMMAND_WORD: - command = new ExitCommand(); + command = new ExitCommand(dataManager, moduleManager, ui); break; default: // Skeletal - Just "echos" back to us. @@ -47,11 +47,10 @@ public static void main(String[] args) { continue; } - command.run(dataManager, moduleManager, ui); + command.run(); } while (!(command instanceof ExitCommand)); - System.out.print("Goodbye!"); System.exit(0); } } diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index ac331c9498..e399f60c18 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -9,8 +9,16 @@ */ public abstract class Command { - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + DataManager dataManager; + ModuleManager moduleManager; + Ui ui; + public Command(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + this.dataManager = dataManager; + this.moduleManager = moduleManager; + this.ui = ui; } + public abstract void run(); + } diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java index 37a0039a15..3fc722aa8d 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -1,6 +1,19 @@ package decodex.commands; +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; + + public ExitCommand(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + super(dataManager, moduleManager, ui); + } + + @Override + public void run() { + System.out.print("Goodbye!"); + } } From 3316fc746bc62210e22de4c022488d490af9d456 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 6 Oct 2021 16:33:43 +0800 Subject: [PATCH 048/607] Resolve suggested changes --- src/test/java/decodex/modules/hex/HexEncoderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/decodex/modules/hex/HexEncoderTest.java b/src/test/java/decodex/modules/hex/HexEncoderTest.java index b1587f6dc0..d61d808a4f 100644 --- a/src/test/java/decodex/modules/hex/HexEncoderTest.java +++ b/src/test/java/decodex/modules/hex/HexEncoderTest.java @@ -22,4 +22,4 @@ void run_allAsciiChars_expectHexEncoding() { assertEquals(expectedString, result.toString()); } -} \ No newline at end of file +} From 4a539de41ad6915c8e82085617b192cbb20904f6 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 6 Oct 2021 16:36:08 +0800 Subject: [PATCH 049/607] Resolve suggested changes --- src/main/java/decodex/modules/hex/HexDecoder.java | 4 +--- src/test/java/decodex/modules/hex/HexDecoderTest.java | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 2917a4428e..dedadff364 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -18,7 +18,6 @@ public class HexDecoder extends BaseModule { public HexDecoder() { super(MODULE_NAME, MODULE_DESCRIPTION); - } /** @@ -29,8 +28,7 @@ public HexDecoder() { */ @Override public Data run(Data data) { - String inputString = data.toString(); - inputString = inputString.replace(" ", ""); + String inputString = data.toString().replace(" ", ""); String decodedString = Arrays .stream(inputString.split(REGEX_SPLIT_EVERY_2_CHARS)) diff --git a/src/test/java/decodex/modules/hex/HexDecoderTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java index a34b299435..edb4b6d54e 100644 --- a/src/test/java/decodex/modules/hex/HexDecoderTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -21,4 +21,4 @@ void run_HexEncoding_expectAllAscii() { assertEquals(expectedString, result.toString()); } -} \ No newline at end of file +} From 99769cb076aa38f2c4df3138cf9a62008447eac4 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 6 Oct 2021 16:37:27 +0800 Subject: [PATCH 050/607] Add ModuleManager --- src/main/java/decodex/data/ModuleManager.java | 44 +++++++++++++++++++ .../data/exception/DummyException.java | 5 --- .../exception/UnknownModuleException.java | 23 ++++++++++ src/main/java/decodex/modules/HexDecoder.java | 15 +++++++ src/main/java/decodex/modules/HexEncoder.java | 15 +++++++ .../java/decodex/data/ModuleManagerTest.java | 40 +++++++++++++++++ 6 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 src/main/java/decodex/data/ModuleManager.java delete mode 100644 src/main/java/decodex/data/exception/DummyException.java create mode 100644 src/main/java/decodex/data/exception/UnknownModuleException.java create mode 100644 src/main/java/decodex/modules/HexDecoder.java create mode 100644 src/main/java/decodex/modules/HexEncoder.java create mode 100644 src/test/java/decodex/data/ModuleManagerTest.java diff --git a/src/main/java/decodex/data/ModuleManager.java b/src/main/java/decodex/data/ModuleManager.java new file mode 100644 index 0000000000..b3414ebb99 --- /dev/null +++ b/src/main/java/decodex/data/ModuleManager.java @@ -0,0 +1,44 @@ +package decodex.data; + +import decodex.data.exception.UnknownModuleException; +import decodex.modules.Base64Decoder; +import decodex.modules.Base64Encoder; +import decodex.modules.BaseModule; +import decodex.modules.HexDecoder; +import decodex.modules.HexEncoder; + +/** + * ModuleManager maintains the list of available modules and returns selected modules. + */ +public class ModuleManager { + + /** + * Array of available modules. + */ + private final BaseModule[] modules = {new Base64Encoder(), new Base64Decoder(), new HexEncoder(), new HexDecoder() + }; + + public ModuleManager() { + } + + public BaseModule[] getModules() { + return modules; + } + + /** + * Select module from a provided module name. + * + * @param moduleName Name of selected module. + * @return Selected module. + * @throws UnknownModuleException Not a module name of an available module. + */ + public BaseModule selectModule(String moduleName) throws UnknownModuleException { + for (BaseModule module : modules) { + if (module.getName().equals(moduleName)) { + return module; + } + } + + throw new UnknownModuleException(moduleName); + } +} diff --git a/src/main/java/decodex/data/exception/DummyException.java b/src/main/java/decodex/data/exception/DummyException.java deleted file mode 100644 index a8d2547d81..0000000000 --- a/src/main/java/decodex/data/exception/DummyException.java +++ /dev/null @@ -1,5 +0,0 @@ -package decodex.data.exception; - -public class DummyException { - -} diff --git a/src/main/java/decodex/data/exception/UnknownModuleException.java b/src/main/java/decodex/data/exception/UnknownModuleException.java new file mode 100644 index 0000000000..c18f5c0bd7 --- /dev/null +++ b/src/main/java/decodex/data/exception/UnknownModuleException.java @@ -0,0 +1,23 @@ +package decodex.data.exception; + +/** + * UnknownModuleException is thrown when an unknown module name is provided. + */ +public class UnknownModuleException extends Exception { + + public static final String EXCEPTION_MESSAGE = "Unknown module name"; + private final String unknownModuleName; + + public UnknownModuleException(String unknownModuleName) { + this.unknownModuleName = unknownModuleName; + } + + public String getUnknownModuleName() { + return unknownModuleName; + } + + @Override + public String getMessage() { + return EXCEPTION_MESSAGE; + } +} diff --git a/src/main/java/decodex/modules/HexDecoder.java b/src/main/java/decodex/modules/HexDecoder.java new file mode 100644 index 0000000000..1278682cb5 --- /dev/null +++ b/src/main/java/decodex/modules/HexDecoder.java @@ -0,0 +1,15 @@ +package decodex.modules; + +import decodex.data.Data; + +public class HexDecoder extends BaseModule { + + public HexDecoder() { + super("hexdecode", "description"); + } + + @Override + public Data run(Data data) { + return null; + } +} diff --git a/src/main/java/decodex/modules/HexEncoder.java b/src/main/java/decodex/modules/HexEncoder.java new file mode 100644 index 0000000000..d760cc3975 --- /dev/null +++ b/src/main/java/decodex/modules/HexEncoder.java @@ -0,0 +1,15 @@ +package decodex.modules; + +import decodex.data.Data; + +public class HexEncoder extends BaseModule { + + public HexEncoder() { + super("hexencode", "description"); + } + + @Override + public Data run(Data data) { + return null; + } +} diff --git a/src/test/java/decodex/data/ModuleManagerTest.java b/src/test/java/decodex/data/ModuleManagerTest.java new file mode 100644 index 0000000000..a3d20f7c5d --- /dev/null +++ b/src/test/java/decodex/data/ModuleManagerTest.java @@ -0,0 +1,40 @@ +package decodex.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import decodex.data.exception.UnknownModuleException; +import decodex.modules.Base64Decoder; +import decodex.modules.Base64Encoder; +import decodex.modules.HexDecoder; +import decodex.modules.HexEncoder; +import org.junit.jupiter.api.Test; + + +public class ModuleManagerTest { + + @Test + public void selectModule_availableModuleName_success() { + ModuleManager moduleManager = new ModuleManager(); + try { + assertTrue(moduleManager.selectModule("base64encode") instanceof Base64Encoder); + assertTrue(moduleManager.selectModule("base64decode") instanceof Base64Decoder); + assertTrue(moduleManager.selectModule("hexencode") instanceof HexEncoder); + assertTrue(moduleManager.selectModule("hexdecode") instanceof HexDecoder); + } catch (UnknownModuleException e) { + fail(); + } + } + + @Test + public void selectModule_unknownModuleName_unknownModuleExceptionThrown() { + ModuleManager moduleManager = new ModuleManager(); + try { + moduleManager.selectModule("unknownModule"); + fail(); + } catch (UnknownModuleException e) { + assertEquals(UnknownModuleException.EXCEPTION_MESSAGE, e.getMessage()); + } + } +} From 6f1b36049c74888c52717e9a6615f08380730988 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 6 Oct 2021 16:46:00 +0800 Subject: [PATCH 051/607] Fix closing bracket position --- src/main/java/decodex/data/ModuleManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/decodex/data/ModuleManager.java b/src/main/java/decodex/data/ModuleManager.java index b3414ebb99..207d99555e 100644 --- a/src/main/java/decodex/data/ModuleManager.java +++ b/src/main/java/decodex/data/ModuleManager.java @@ -15,8 +15,7 @@ public class ModuleManager { /** * Array of available modules. */ - private final BaseModule[] modules = {new Base64Encoder(), new Base64Decoder(), new HexEncoder(), new HexDecoder() - }; + private final BaseModule[] modules = {new Base64Encoder(), new Base64Decoder(), new HexEncoder(), new HexDecoder()}; public ModuleManager() { } From c5ee26acdbab1707d24ed45897b3f635148cf061 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 6 Oct 2021 17:42:45 +0800 Subject: [PATCH 052/607] Modify import statements --- src/main/java/decodex/data/ModuleManager.java | 8 ++++---- src/main/java/decodex/modules/HexDecoder.java | 15 --------------- src/main/java/decodex/modules/HexEncoder.java | 15 --------------- src/test/java/decodex/data/ModuleManagerTest.java | 8 ++++---- 4 files changed, 8 insertions(+), 38 deletions(-) delete mode 100644 src/main/java/decodex/modules/HexDecoder.java delete mode 100644 src/main/java/decodex/modules/HexEncoder.java diff --git a/src/main/java/decodex/data/ModuleManager.java b/src/main/java/decodex/data/ModuleManager.java index 207d99555e..a0e2032164 100644 --- a/src/main/java/decodex/data/ModuleManager.java +++ b/src/main/java/decodex/data/ModuleManager.java @@ -1,11 +1,11 @@ package decodex.data; import decodex.data.exception.UnknownModuleException; -import decodex.modules.Base64Decoder; -import decodex.modules.Base64Encoder; import decodex.modules.BaseModule; -import decodex.modules.HexDecoder; -import decodex.modules.HexEncoder; +import decodex.modules.base64.Base64Decoder; +import decodex.modules.base64.Base64Encoder; +import decodex.modules.hex.HexDecoder; +import decodex.modules.hex.HexEncoder; /** * ModuleManager maintains the list of available modules and returns selected modules. diff --git a/src/main/java/decodex/modules/HexDecoder.java b/src/main/java/decodex/modules/HexDecoder.java deleted file mode 100644 index 1278682cb5..0000000000 --- a/src/main/java/decodex/modules/HexDecoder.java +++ /dev/null @@ -1,15 +0,0 @@ -package decodex.modules; - -import decodex.data.Data; - -public class HexDecoder extends BaseModule { - - public HexDecoder() { - super("hexdecode", "description"); - } - - @Override - public Data run(Data data) { - return null; - } -} diff --git a/src/main/java/decodex/modules/HexEncoder.java b/src/main/java/decodex/modules/HexEncoder.java deleted file mode 100644 index d760cc3975..0000000000 --- a/src/main/java/decodex/modules/HexEncoder.java +++ /dev/null @@ -1,15 +0,0 @@ -package decodex.modules; - -import decodex.data.Data; - -public class HexEncoder extends BaseModule { - - public HexEncoder() { - super("hexencode", "description"); - } - - @Override - public Data run(Data data) { - return null; - } -} diff --git a/src/test/java/decodex/data/ModuleManagerTest.java b/src/test/java/decodex/data/ModuleManagerTest.java index a3d20f7c5d..b187428014 100644 --- a/src/test/java/decodex/data/ModuleManagerTest.java +++ b/src/test/java/decodex/data/ModuleManagerTest.java @@ -5,10 +5,10 @@ import static org.junit.jupiter.api.Assertions.fail; import decodex.data.exception.UnknownModuleException; -import decodex.modules.Base64Decoder; -import decodex.modules.Base64Encoder; -import decodex.modules.HexDecoder; -import decodex.modules.HexEncoder; +import decodex.modules.base64.Base64Decoder; +import decodex.modules.base64.Base64Encoder; +import decodex.modules.hex.HexDecoder; +import decodex.modules.hex.HexEncoder; import org.junit.jupiter.api.Test; From afdca81951660a9491fcc61b92185142569bed21 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 6 Oct 2021 18:26:38 +0800 Subject: [PATCH 053/607] Remove need for starting data for DataManager constructor --- src/main/java/decodex/data/DataManager.java | 32 +++++++-- .../data/exception/DataManagerException.java | 11 ++++ .../data/exception/DummyException.java | 5 -- .../java/decodex/data/DataManagerTest.java | 65 +++++++++++++++---- 4 files changed, 90 insertions(+), 23 deletions(-) create mode 100644 src/main/java/decodex/data/exception/DataManagerException.java delete mode 100644 src/main/java/decodex/data/exception/DummyException.java diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index afe4ba7b6e..fa571bcf75 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -1,26 +1,46 @@ package decodex.data; +import decodex.data.exception.DataManagerException; + /** * The DataManager class manages the current state of the data. */ public class DataManager { /* Data provided by the user */ - private final Data originalData; + private Data originalData; /* Data to perform the encoding/decoding operations on */ private Data currentData; - public DataManager(Data originalData) { - this.originalData = originalData; - currentData = originalData; + public DataManager() { + originalData = null; + currentData = null; + } + + public Data getOriginalData() throws DataManagerException { + if (originalData == null) { + throw new DataManagerException("No data found"); + } + return originalData; } - public Data getCurrentData() { + public Data getCurrentData() throws DataManagerException { + if (currentData == null) { + throw new DataManagerException("No data found"); + } return currentData; } - public void setCurrentData(Data currentData) { + public void setOriginalData(Data originalData) { + this.originalData = originalData; + resetToOriginalData(); + } + + public void setCurrentData(Data currentData) throws DataManagerException { + if (originalData == null) { + throw new DataManagerException("No data found"); + } this.currentData = currentData; } diff --git a/src/main/java/decodex/data/exception/DataManagerException.java b/src/main/java/decodex/data/exception/DataManagerException.java new file mode 100644 index 0000000000..75682e20fa --- /dev/null +++ b/src/main/java/decodex/data/exception/DataManagerException.java @@ -0,0 +1,11 @@ +package decodex.data.exception; + +/** + * This exception is raised when an error during access to the DataManager occurs. + */ +public class DataManagerException extends Exception { + + public DataManagerException(String message) { + super(message); + } +} diff --git a/src/main/java/decodex/data/exception/DummyException.java b/src/main/java/decodex/data/exception/DummyException.java deleted file mode 100644 index a8d2547d81..0000000000 --- a/src/main/java/decodex/data/exception/DummyException.java +++ /dev/null @@ -1,5 +0,0 @@ -package decodex.data.exception; - -public class DummyException { - -} diff --git a/src/test/java/decodex/data/DataManagerTest.java b/src/test/java/decodex/data/DataManagerTest.java index ead43a24d4..1ba60148e4 100644 --- a/src/test/java/decodex/data/DataManagerTest.java +++ b/src/test/java/decodex/data/DataManagerTest.java @@ -1,36 +1,77 @@ package decodex.data; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.Arrays; +import decodex.data.exception.DataManagerException; +import java.util.Arrays; import org.junit.jupiter.api.Test; class DataManagerTest { @Test - public void resetCurrentData_noUpdates_originalData() { - Data originalData = new Data("hi"); - DataManager testManager = new DataManager(originalData); + public void getOriginalData_noData_expectException() { + DataManager testManager = new DataManager(); + assertThrows(DataManagerException.class, () -> testManager.getOriginalData()); + } - testManager.resetToOriginalData(); + @Test + public void getCurrentData_noData_expectException() { + DataManager testManager = new DataManager(); + assertThrows(DataManagerException.class, () -> testManager.getCurrentData()); + } - Data currentData = testManager.getCurrentData(); + @Test + public void getCurrentData_withOriginalData_originalData() throws DataManagerException { + DataManager testManager = new DataManager(); + Data originalData = new Data("hi"); + testManager.setOriginalData(originalData); + assertTrue(Arrays.equals(testManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); + } + + @Test + public void setOriginalData_withOriginalData_originalData() throws DataManagerException { + DataManager testManager = new DataManager(); + Data originalData = new Data("hi"); + testManager.setOriginalData(originalData); + assertTrue(Arrays.equals(testManager.getOriginalData().getRawBytes(), originalData.getRawBytes())); + } - assertTrue(Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes())); + @Test + public void setCurrentData_noData_expectException() { + DataManager testManager = new DataManager(); + Data newData = new Data("bye"); + assertThrows(DataManagerException.class, () -> testManager.setCurrentData(newData)); } @Test - public void resetCurrentData_updateWithNewData_originalData() { + public void setCurrentData_newData_newData() throws DataManagerException { + DataManager testManager = new DataManager(); Data originalData = new Data("hi"); + testManager.setOriginalData(originalData); Data newData = new Data("bye"); - DataManager testManager = new DataManager(originalData); testManager.setCurrentData(newData); + assertTrue(Arrays.equals(testManager.getCurrentData().getRawBytes(), newData.getRawBytes())); + } + @Test + public void resetToOriginalData_noUpdates_originalData() throws DataManagerException { + DataManager testManager = new DataManager(); + Data originalData = new Data("hi"); + testManager.setOriginalData(originalData); testManager.resetToOriginalData(); + assertTrue(Arrays.equals(testManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); + } - Data currentData = testManager.getCurrentData(); - - assertTrue(Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes())); + @Test + public void resetToOriginalData_newData_originalData() throws DataManagerException { + DataManager testManager = new DataManager(); + Data originalData = new Data("hi"); + testManager.setOriginalData(originalData); + Data newData = new Data("bye"); + testManager.setCurrentData(newData); + testManager.resetToOriginalData(); + assertTrue(Arrays.equals(testManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); } } From 4840deae0490fb35c15ba5fab435cc778ab061e7 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 6 Oct 2021 19:33:54 +0800 Subject: [PATCH 054/607] Simplify UnknownModuleException --- .../java/decodex/data/exception/UnknownModuleException.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/decodex/data/exception/UnknownModuleException.java b/src/main/java/decodex/data/exception/UnknownModuleException.java index c18f5c0bd7..091717b699 100644 --- a/src/main/java/decodex/data/exception/UnknownModuleException.java +++ b/src/main/java/decodex/data/exception/UnknownModuleException.java @@ -9,15 +9,11 @@ public class UnknownModuleException extends Exception { private final String unknownModuleName; public UnknownModuleException(String unknownModuleName) { + super(EXCEPTION_MESSAGE); this.unknownModuleName = unknownModuleName; } public String getUnknownModuleName() { return unknownModuleName; } - - @Override - public String getMessage() { - return EXCEPTION_MESSAGE; - } } From 5acb8cf0ba4b89fe34c8937c9eabddcabdfad5e4 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Thu, 7 Oct 2021 02:09:27 +0800 Subject: [PATCH 055/607] Resolve suggested changes --- src/main/java/decodex/commands/Command.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index e399f60c18..83fa1f0eee 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -9,9 +9,9 @@ */ public abstract class Command { - DataManager dataManager; - ModuleManager moduleManager; - Ui ui; + protected DataManager dataManager; + protected ModuleManager moduleManager; + protected Ui ui; public Command(DataManager dataManager, ModuleManager moduleManager, Ui ui) { this.dataManager = dataManager; From f1dfdca76a85169c57c920fe2d8be25d7d087959 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 7 Oct 2021 02:13:18 +0800 Subject: [PATCH 056/607] Move Module Manager from decodex.data to decodex.modules --- .../java/decodex/{data => modules}/ModuleManager.java | 3 +-- .../decodex/{data => modules}/ModuleManagerTest.java | 11 +++++------ 2 files changed, 6 insertions(+), 8 deletions(-) rename src/main/java/decodex/{data => modules}/ModuleManager.java (95%) rename src/test/java/decodex/{data => modules}/ModuleManagerTest.java (98%) diff --git a/src/main/java/decodex/data/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java similarity index 95% rename from src/main/java/decodex/data/ModuleManager.java rename to src/main/java/decodex/modules/ModuleManager.java index a0e2032164..8b81725bb1 100644 --- a/src/main/java/decodex/data/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -1,7 +1,6 @@ -package decodex.data; +package decodex.modules; import decodex.data.exception.UnknownModuleException; -import decodex.modules.BaseModule; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexDecoder; diff --git a/src/test/java/decodex/data/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java similarity index 98% rename from src/test/java/decodex/data/ModuleManagerTest.java rename to src/test/java/decodex/modules/ModuleManagerTest.java index b187428014..dd39f87061 100644 --- a/src/test/java/decodex/data/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -1,16 +1,15 @@ -package decodex.data; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +package decodex.modules; import decodex.data.exception.UnknownModuleException; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; public class ModuleManagerTest { From 28254dc90b7e623b2512dc39071d1255410db8cd Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 09:38:00 +0800 Subject: [PATCH 057/607] Moved some of the necessary objects to be initialized within a seperate init method. Also abstracted some of the printing related code into their seperate methods, for easier moving over to UI class. --- src/main/java/decodex/Decodex.java | 43 ++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index d5d4e0a22a..2a39d75e65 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -15,7 +15,12 @@ public class Decodex { /** * Necessary objects to be initialized for Decodex to work properly. */ + private static DataManager dataManager; + private static ModuleManager moduleManager; private static Parser parser; + private static Scanner in; + private static Ui ui; + /** * Logo to be changed - possibly to a welcome message instead for Decodex. @@ -30,38 +35,36 @@ public class Decodex { * Initializes the necessary Objects for Decodex. */ public static void initDecodex() { + dataManager = new DataManager(); + moduleManager = new ModuleManager(); parser = new Parser(); + in = new Scanner(System.in); + ui = new Ui(); } /** * Decodex entry-point for the java.decodex.Decodex application. */ public static void main(String[] args) { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Ui ui = new Ui(); - - System.out.println("Hello from\n" + LOGO); + printGreeting(); initDecodex(); - Scanner in = new Scanner(System.in); - // Temporary code, command functions will be moved to parser Command command = null; do { - System.out.print("Decodex > "); + printPromptHeader(); String userInput = in.nextLine(); - //String command = parser.getCommandType(userInput); + String commandType = parser.getCommandType(userInput); //String userArgument = parser.getUserArgument(userInput); - switch (userInput) { + switch (commandType) { case ExitCommand.COMMAND_WORD: command = new ExitCommand(dataManager, moduleManager, ui); break; default: // Skeletal - Just "echos" back to us. - System.out.println(userInput); + echoUserInput(userInput); continue; } @@ -71,4 +74,22 @@ public static void main(String[] args) { System.exit(0); } + + /** + * Prints the greeting message. + */ + public static void printGreeting() { + System.out.println("Hello from\n" + LOGO); + } + + /** + * Prints the prompt header. + */ + public static void printPromptHeader() { + System.out.print("Decodex > "); + } + + public static void echoUserInput(String userInput) { + System.out.println(userInput); + } } From b99c86f9384a7d69995ccfa042fb41c68d81fd2e Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 11:37:41 +0800 Subject: [PATCH 058/607] Added/moved the command parsing logic over to Parser Class. --- src/main/java/decodex/Decodex.java | 15 +++--------- src/main/java/decodex/parser/Parser.java | 31 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 2a39d75e65..e90ab0247c 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -55,21 +55,14 @@ public static void main(String[] args) { do { printPromptHeader(); String userInput = in.nextLine(); - String commandType = parser.getCommandType(userInput); - //String userArgument = parser.getUserArgument(userInput); - - switch (commandType) { - case ExitCommand.COMMAND_WORD: - command = new ExitCommand(dataManager, moduleManager, ui); - break; - default: - // Skeletal - Just "echos" back to us. + + command = parser.parseCommand(userInput, dataManager, moduleManager, ui); + + if (command == null) { echoUserInput(userInput); continue; } - command.run(); - } while (!(command instanceof ExitCommand)); System.exit(0); diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 63977ee814..9a48c6e33c 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -1,5 +1,11 @@ package decodex.parser; +import decodex.commands.Command; +import decodex.commands.ExitCommand; +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + /** * Parses and validates the user input. */ @@ -33,4 +39,29 @@ public String getUserArgument(String userInput) { return ""; } } + + /** + * Parses the user input provided by user and returns its respective command. + * Returns a null object if the command is invalid. + * + * @param userInput The user input specified by the user. + * @param dataManager The DataManager object + * @param moduleManager The ModuleManager object + * @param ui The Ui object + * @return The respective Command object. + */ + public Command parseCommand(String userInput, DataManager dataManager, ModuleManager moduleManager, Ui ui) { + Command command; + String commandType = getCommandType(userInput); + + switch (commandType) { + case ExitCommand.COMMAND_WORD: + command = new ExitCommand(dataManager, moduleManager, ui); + break; + default: + return null; + } + + return command; + } } From 5896898b2e740216c84540c44b95ce326b74958a Mon Sep 17 00:00:00 2001 From: rizemon Date: Thu, 7 Oct 2021 11:51:41 +0800 Subject: [PATCH 059/607] Add DataCommand --- src/main/java/decodex/Decodex.java | 20 +++++++-- src/main/java/decodex/commands/Command.java | 3 +- .../java/decodex/commands/DataCommand.java | 31 +++++++++++++ .../data/exception/CommandException.java | 11 +++++ .../decodex/commands/DataCommandTest.java | 43 +++++++++++++++++++ text-ui-test/EXPECTED.TXT | 2 + text-ui-test/input.txt | 2 + 7 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 src/main/java/decodex/commands/DataCommand.java create mode 100644 src/main/java/decodex/data/exception/CommandException.java create mode 100644 src/test/java/decodex/commands/DataCommandTest.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 44e9f16a6b..d4511d1217 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -1,10 +1,13 @@ package decodex; import decodex.commands.Command; +import decodex.commands.DataCommand; import decodex.commands.ExitCommand; import decodex.data.DataManager; +import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.ui.Ui; +import java.util.Arrays; import java.util.Scanner; public class Decodex { @@ -36,8 +39,16 @@ public static void main(String[] args) { do { System.out.print("Decodex > "); String userInput = in.nextLine(); + String[] tokens = userInput.split(" "); + if (tokens.length < 1) { + continue; + } + String[] arguments = Arrays.copyOfRange(tokens,1, tokens.length); - switch (userInput) { + switch (tokens[0]) { + case DataCommand.COMMAND_WORD: + command = new DataCommand(dataManager, moduleManager, ui, arguments); + break; case ExitCommand.COMMAND_WORD: command = new ExitCommand(dataManager, moduleManager, ui); break; @@ -46,8 +57,11 @@ public static void main(String[] args) { System.out.println(userInput); continue; } - - command.run(); + try { + command.run(); + } catch (CommandException err) { + System.out.println(err.getMessage()); + } } while (!(command instanceof ExitCommand)); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index 83fa1f0eee..170cdeaff9 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -1,6 +1,7 @@ package decodex.commands; import decodex.data.DataManager; +import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -19,6 +20,6 @@ public Command(DataManager dataManager, ModuleManager moduleManager, Ui ui) { this.ui = ui; } - public abstract void run(); + public abstract void run() throws CommandException; } diff --git a/src/main/java/decodex/commands/DataCommand.java b/src/main/java/decodex/commands/DataCommand.java new file mode 100644 index 0000000000..c92d6ae5ab --- /dev/null +++ b/src/main/java/decodex/commands/DataCommand.java @@ -0,0 +1,31 @@ +package decodex.commands; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +public class DataCommand extends Command { + + public static final String COMMAND_WORD = "data"; + public static final int DATA_STRING_INDEX = 0; + public static final int ARGUMENT_COUNT = 1; + private String[] arguments; + + public DataCommand(DataManager dataManager, ModuleManager moduleManager, Ui ui, String[] arguments) { + super(dataManager, moduleManager, ui); + this.arguments = arguments; + } + + @Override + public void run() throws CommandException { + if (arguments.length < ARGUMENT_COUNT) { + throw new CommandException("[-] Missing arguments"); + } + String dataString = arguments[DATA_STRING_INDEX]; + Data userData = new Data(dataString); + dataManager.setOriginalData(userData); + System.out.printf("[+] Input: %s\n", dataString); + } +} diff --git a/src/main/java/decodex/data/exception/CommandException.java b/src/main/java/decodex/data/exception/CommandException.java new file mode 100644 index 0000000000..574a1c592b --- /dev/null +++ b/src/main/java/decodex/data/exception/CommandException.java @@ -0,0 +1,11 @@ +package decodex.data.exception; + +/** + * This exception is raised when an error during command execution occurs. + */ +public class CommandException extends Exception { + + public CommandException(String message) { + super(message); + } +} diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java new file mode 100644 index 0000000000..61c5d6e691 --- /dev/null +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -0,0 +1,43 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +public class DataCommandTest { + + @Test + public void run_missingArguments_expectException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + String[] testArguments = {}; + Command testCommand = new DataCommand(dataManager, moduleManager, ui, testArguments); + + assertThrows(CommandException.class, () -> testCommand.run()); + } + + @Test + public void run_insertData_insertedData() throws CommandException, DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + String dataString = "something"; + String[] parsedArguments = {dataString}; + Command testCommand = new DataCommand(dataManager, moduleManager, ui, parsedArguments); + testCommand.run(); + + Data testData = new Data(dataString); + assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); + } +} \ No newline at end of file diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 4679efac27..78510359e8 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -8,5 +8,7 @@ Hello from Decodex > whoami Decodex > bring me thanos Decodex > hi +Decodex > [-] Missing arguments +Decodex > [+] Input: something Decodex > bye Decodex > Goodbye! \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 4c09ae32bc..6ffa937c0d 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,5 +1,7 @@ whoami bring me thanos hi +data +data something bye exit \ No newline at end of file From 4c9d77b99596752b383b9037c6afa7ebee9abcea Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 11:58:22 +0800 Subject: [PATCH 060/607] Removed redundant lines of code. And added additional JUnit test for Parser. --- src/main/java/decodex/parser/Parser.java | 7 ++--- src/test/java/decodex/parser/ParserTest.java | 29 +++++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 9a48c6e33c..f86807f7a5 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -19,8 +19,7 @@ public class Parser { */ public String getCommandType(String userInput) { String[] tokens = userInput.split(" "); - String commandType = tokens[0]; - return commandType; + return tokens[0]; } /** @@ -32,8 +31,7 @@ public String getCommandType(String userInput) { public String getUserArgument(String userInput) { try { String[] tokens = userInput.split(" "); - String userArgument = tokens[1]; - return userArgument; + return tokens[1]; } catch (ArrayIndexOutOfBoundsException err) { // Temporary error handling return ""; @@ -61,7 +59,6 @@ public Command parseCommand(String userInput, DataManager dataManager, ModuleMan default: return null; } - return command; } } diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 1236fbe9ce..7f3510027e 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -1,18 +1,30 @@ package decodex.parser; +import decodex.commands.Command; +import decodex.commands.ExitCommand; +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class ParserTest { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Parser parser = new Parser(); + Ui ui = new Ui(); + @Test void getCommandType_stringWithNoSeparator_expectCommandTypeString() { String testInput = "test"; String expectedOutput = "test"; - Parser parser = new Parser(); String commandType = parser.getCommandType(testInput); + assertEquals(expectedOutput, commandType); } @@ -20,9 +32,9 @@ void getCommandType_stringWithNoSeparator_expectCommandTypeString() { void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() { String testInput = "test data"; String expectedOutput = "test"; - Parser parser = new Parser(); String commandType = parser.getCommandType(testInput); + assertEquals(expectedOutput, commandType); } @@ -30,9 +42,18 @@ void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() { void getUserArgument_userInputWithOneSpaceSeparator_expectArgumentString() { String testInput = "test data"; String expectedOutput = "data"; - Parser parser = new Parser(); String argumentString = parser.getUserArgument(testInput); + assertEquals(expectedOutput, argumentString); } -} \ No newline at end of file + + @Test + void parseCommand_userInputSpecifyingExit_expectExitCommand() { + String userInput = "exit"; + + Command command = parser.parseCommand(userInput, dataManager, moduleManager, ui); + + assertTrue(command instanceof ExitCommand); + } +} From 24098039ce76bb02da0d3a70cb6f14a06ed3ed54 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 12:59:59 +0800 Subject: [PATCH 061/607] Made changes to Parser's getUserArgument to getUserArguments, to return an array of arguments instead of just a single one. Also made changes to it's corresponding JUnit test method. --- src/main/java/decodex/parser/Parser.java | 8 +++++--- src/test/java/decodex/parser/ParserTest.java | 9 +++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index f86807f7a5..410f1f7980 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -6,6 +6,8 @@ import decodex.modules.ModuleManager; import decodex.ui.Ui; +import java.util.Arrays; + /** * Parses and validates the user input. */ @@ -28,13 +30,13 @@ public String getCommandType(String userInput) { * @param userInput The input specified by the user. * @return The argument portion of the user input. */ - public String getUserArgument(String userInput) { + public String[] getUserArguments(String userInput) { try { String[] tokens = userInput.split(" "); - return tokens[1]; + return Arrays.copyOfRange(tokens,1, tokens.length); } catch (ArrayIndexOutOfBoundsException err) { // Temporary error handling - return ""; + return null; } } diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 7f3510027e..d8541c5fde 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -39,13 +40,13 @@ void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() { } @Test - void getUserArgument_userInputWithOneSpaceSeparator_expectArgumentString() { + void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() { String testInput = "test data"; - String expectedOutput = "data"; + String[] expectedOutput = new String[]{"data"}; - String argumentString = parser.getUserArgument(testInput); + String[] arguments = parser.getUserArguments(testInput); - assertEquals(expectedOutput, argumentString); + assertArrayEquals(expectedOutput, arguments); } @Test From a1f80087ac053056df59d34d83077df631bc2d15 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 17:15:42 +0800 Subject: [PATCH 062/607] Added the additional spacing in front of the '1'. --- src/main/java/decodex/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 410f1f7980..c0fc1739a9 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -33,7 +33,7 @@ public String getCommandType(String userInput) { public String[] getUserArguments(String userInput) { try { String[] tokens = userInput.split(" "); - return Arrays.copyOfRange(tokens,1, tokens.length); + return Arrays.copyOfRange(tokens, 1, tokens.length); } catch (ArrayIndexOutOfBoundsException err) { // Temporary error handling return null; From 2995b8b4f3ac63057c6c648aea95e6932c5000d7 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 17:16:24 +0800 Subject: [PATCH 063/607] Added a dummy ParserException which I will be working on tomorrow morning. --- src/main/java/decodex/parser/exception/ParserException.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/decodex/parser/exception/ParserException.java diff --git a/src/main/java/decodex/parser/exception/ParserException.java b/src/main/java/decodex/parser/exception/ParserException.java new file mode 100644 index 0000000000..7c5408365a --- /dev/null +++ b/src/main/java/decodex/parser/exception/ParserException.java @@ -0,0 +1,5 @@ +package decodex.parser.exception; + +public class ParserException extends Exception { + // dummy exception class, will do tomorrow morning. +} From 235a1d41eb1e4fff6c6cf589ada66d1b43c35d58 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 17:49:15 +0800 Subject: [PATCH 064/607] Decided to just do it now, getting triggered by CS2102's assignment. --- .../java/decodex/parser/exception/ParserException.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/exception/ParserException.java b/src/main/java/decodex/parser/exception/ParserException.java index 7c5408365a..797cd85ddb 100644 --- a/src/main/java/decodex/parser/exception/ParserException.java +++ b/src/main/java/decodex/parser/exception/ParserException.java @@ -1,5 +1,12 @@ package decodex.parser.exception; public class ParserException extends Exception { - // dummy exception class, will do tomorrow morning. + + public static final String MISSING_COMMAND_TYPE_MESSAGE = "Command cannot be empty!!"; + public static final String MISSING_COMMAND_ARGS_MESSAGE = "Your command arguments is empty." + + " Please try again after tone...BEEP"; + + public ParserException(String message) { + super(message); + } } From 71718035ecfb78b349fc99f9adaa5ca45681fdc5 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 17:50:30 +0800 Subject: [PATCH 065/607] Modified the Parser functions to throw ParserException upon checks and Decodex.java to catch for ParserExceptions. --- src/main/java/decodex/Decodex.java | 19 +++++++----- src/main/java/decodex/parser/Parser.java | 38 ++++++++++++++---------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index e90ab0247c..2d45bbad9d 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -6,6 +6,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.parser.Parser; +import decodex.parser.exception.ParserException; import decodex.ui.Ui; import java.util.Scanner; @@ -49,17 +50,16 @@ public static void main(String[] args) { printGreeting(); initDecodex(); - // Temporary code, command functions will be moved to parser Command command = null; do { printPromptHeader(); String userInput = in.nextLine(); - command = parser.parseCommand(userInput, dataManager, moduleManager, ui); - - if (command == null) { - echoUserInput(userInput); + try { + command = parser.parseCommand(userInput, dataManager, moduleManager, ui); + } catch (ParserException err) { + printErrorMessage(err.getMessage()); continue; } command.run(); @@ -82,7 +82,12 @@ public static void printPromptHeader() { System.out.print("Decodex > "); } - public static void echoUserInput(String userInput) { - System.out.println(userInput); + /** + * Prints the handled error messages. + * + * @param errorMessage The respective error message. + */ + public static void printErrorMessage(String errorMessage) { + System.out.println(errorMessage); } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index c0fc1739a9..135c96bbf1 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -4,6 +4,7 @@ import decodex.commands.ExitCommand; import decodex.data.DataManager; import decodex.modules.ModuleManager; +import decodex.parser.exception.ParserException; import decodex.ui.Ui; import java.util.Arrays; @@ -19,8 +20,12 @@ public class Parser { * @param userInput The input specified by the user. * @return The type of command. */ - public String getCommandType(String userInput) { + public String getCommandType(String userInput) throws ParserException { String[] tokens = userInput.split(" "); + String commandType = tokens[0]; + if (commandType.isEmpty()) { + throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); + } return tokens[0]; } @@ -30,13 +35,12 @@ public String getCommandType(String userInput) { * @param userInput The input specified by the user. * @return The argument portion of the user input. */ - public String[] getUserArguments(String userInput) { + public String[] getUserArguments(String userInput) throws ParserException { try { String[] tokens = userInput.split(" "); return Arrays.copyOfRange(tokens, 1, tokens.length); } catch (ArrayIndexOutOfBoundsException err) { - // Temporary error handling - return null; + throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); } } @@ -44,22 +48,26 @@ public String[] getUserArguments(String userInput) { * Parses the user input provided by user and returns its respective command. * Returns a null object if the command is invalid. * - * @param userInput The user input specified by the user. - * @param dataManager The DataManager object + * @param userInput The user input specified by the user. + * @param dataManager The DataManager object * @param moduleManager The ModuleManager object - * @param ui The Ui object + * @param ui The Ui object * @return The respective Command object. */ - public Command parseCommand(String userInput, DataManager dataManager, ModuleManager moduleManager, Ui ui) { + public Command parseCommand(String userInput, DataManager dataManager, ModuleManager moduleManager, Ui ui) throws ParserException { Command command; - String commandType = getCommandType(userInput); + try { + String commandType = getCommandType(userInput); - switch (commandType) { - case ExitCommand.COMMAND_WORD: - command = new ExitCommand(dataManager, moduleManager, ui); - break; - default: - return null; + switch (commandType) { + case ExitCommand.COMMAND_WORD: + command = new ExitCommand(dataManager, moduleManager, ui); + break; + default: + throw new ParserException(userInput + " !To be replaced with invalid command message!"); + } + } catch (ParserException err) { + throw new ParserException(err.getMessage()); } return command; } From 0a6314fb2367aa0dad3169978328989082066242 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 17:51:16 +0800 Subject: [PATCH 066/607] Updated the related problems in terms of the newly added ParserExceptions handling in the ParserTest JUnit file. --- src/test/java/decodex/parser/ParserTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index d8541c5fde..b19909ec23 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -4,6 +4,7 @@ import decodex.commands.ExitCommand; import decodex.data.DataManager; import decodex.modules.ModuleManager; +import decodex.parser.exception.ParserException; import decodex.ui.Ui; import org.junit.jupiter.api.Test; @@ -20,7 +21,7 @@ class ParserTest { Ui ui = new Ui(); @Test - void getCommandType_stringWithNoSeparator_expectCommandTypeString() { + void getCommandType_stringWithNoSeparator_expectCommandTypeString() throws ParserException { String testInput = "test"; String expectedOutput = "test"; @@ -30,7 +31,7 @@ void getCommandType_stringWithNoSeparator_expectCommandTypeString() { } @Test - void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() { + void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() throws ParserException { String testInput = "test data"; String expectedOutput = "test"; @@ -40,7 +41,7 @@ void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() { } @Test - void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() { + void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() throws ParserException { String testInput = "test data"; String[] expectedOutput = new String[]{"data"}; @@ -50,7 +51,7 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() } @Test - void parseCommand_userInputSpecifyingExit_expectExitCommand() { + void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserException { String userInput = "exit"; Command command = parser.parseCommand(userInput, dataManager, moduleManager, ui); From 2c4b2904db81043759a07d2c8e2e95cdc95f947a Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 17:51:56 +0800 Subject: [PATCH 067/607] Updated the corresponding text ui test according to the new error messages of the ParserException. --- text-ui-test/EXPECTED.TXT | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 4679efac27..a10b321cad 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,8 +5,8 @@ Hello from | |_| | |_| | < __/ |____/ \__,_|_|\_\___| -Decodex > whoami -Decodex > bring me thanos -Decodex > hi -Decodex > bye +Decodex > whoami !To be replaced with invalid command message! +Decodex > bring me thanos !To be replaced with invalid command message! +Decodex > hi !To be replaced with invalid command message! +Decodex > bye !To be replaced with invalid command message! Decodex > Goodbye! \ No newline at end of file From 3adb6c8def07795c8aac346d392febd881ea9438 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 17:54:23 +0800 Subject: [PATCH 068/607] Changes to line 57 - method signature of the Parser.java as it was longer than 120 chars. --- src/main/java/decodex/parser/Parser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 135c96bbf1..cfd452bcdc 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -54,7 +54,8 @@ public String[] getUserArguments(String userInput) throws ParserException { * @param ui The Ui object * @return The respective Command object. */ - public Command parseCommand(String userInput, DataManager dataManager, ModuleManager moduleManager, Ui ui) throws ParserException { + public Command parseCommand(String userInput, DataManager dataManager, ModuleManager moduleManager, Ui ui) + throws ParserException { Command command; try { String commandType = getCommandType(userInput); From 754f3e024d7e38190c33d824d1496a31caa9defa Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 18:29:56 +0800 Subject: [PATCH 069/607] Removed the redundant try-catch to rethrow the ParserException. --- src/main/java/decodex/parser/Parser.java | 31 ++++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index cfd452bcdc..364539fa34 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -21,11 +21,19 @@ public class Parser { * @return The type of command. */ public String getCommandType(String userInput) throws ParserException { + if (userInput.isEmpty()) { + throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); + } + String[] tokens = userInput.split(" "); String commandType = tokens[0]; - if (commandType.isEmpty()) { - throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); + + boolean isInvalidTokensLength = tokens.length < 1; + + if (isInvalidTokensLength) { + throw new ParserException(ParserException.WEIRD_COMMAND_TYPE_MESSAGE); } + return tokens[0]; } @@ -57,18 +65,15 @@ public String[] getUserArguments(String userInput) throws ParserException { public Command parseCommand(String userInput, DataManager dataManager, ModuleManager moduleManager, Ui ui) throws ParserException { Command command; - try { - String commandType = getCommandType(userInput); - switch (commandType) { - case ExitCommand.COMMAND_WORD: - command = new ExitCommand(dataManager, moduleManager, ui); - break; - default: - throw new ParserException(userInput + " !To be replaced with invalid command message!"); - } - } catch (ParserException err) { - throw new ParserException(err.getMessage()); + String commandType = getCommandType(userInput); + + switch (commandType) { + case ExitCommand.COMMAND_WORD: + command = new ExitCommand(dataManager, moduleManager, ui); + break; + default: + throw new ParserException(userInput + " !To be replaced with invalid command message!"); } return command; } From d3cf2599977ea03b67531be146274d466f0f9769 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 18:39:42 +0800 Subject: [PATCH 070/607] Changes to the error handling logic. (Mostly to avoid rethrowing of exceptions) --- src/main/java/decodex/parser/Parser.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 364539fa34..3af622dfa5 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -26,14 +26,12 @@ public String getCommandType(String userInput) throws ParserException { } String[] tokens = userInput.split(" "); - String commandType = tokens[0]; boolean isInvalidTokensLength = tokens.length < 1; if (isInvalidTokensLength) { throw new ParserException(ParserException.WEIRD_COMMAND_TYPE_MESSAGE); } - return tokens[0]; } @@ -44,12 +42,12 @@ public String getCommandType(String userInput) throws ParserException { * @return The argument portion of the user input. */ public String[] getUserArguments(String userInput) throws ParserException { - try { - String[] tokens = userInput.split(" "); - return Arrays.copyOfRange(tokens, 1, tokens.length); - } catch (ArrayIndexOutOfBoundsException err) { + String[] tokens = userInput.split(" "); + + if (tokens.length <= 1) { throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); } + return Arrays.copyOfRange(tokens, 1, tokens.length); } /** From e7f10de06d0fd1788a940afea4a06879cffb24a6 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 18:43:26 +0800 Subject: [PATCH 071/607] Added another "easter egg" error message targeted towards "bug finders". For now, this message is triggered when space is give as the command. This results in tokens being length 0, as opposed to empty command that gives length of 0 with tokens[0] being "". Some of the error messages will be changed in future commits. --- .../java/decodex/parser/exception/ParserException.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/exception/ParserException.java b/src/main/java/decodex/parser/exception/ParserException.java index 797cd85ddb..ef15672059 100644 --- a/src/main/java/decodex/parser/exception/ParserException.java +++ b/src/main/java/decodex/parser/exception/ParserException.java @@ -2,9 +2,15 @@ public class ParserException extends Exception { - public static final String MISSING_COMMAND_TYPE_MESSAGE = "Command cannot be empty!!"; public static final String MISSING_COMMAND_ARGS_MESSAGE = "Your command arguments is empty." + " Please try again after tone...BEEP"; + public static final String MISSING_COMMAND_TYPE_MESSAGE = "Command cannot be empty!!"; + + /** + * This error message is dedicated to users intending to find bugs in our program. + */ + public static final String WEIRD_COMMAND_TYPE_MESSAGE = "In what universe did you think that would break us." + + " Please try again and use the valid commands, thanks."; public ParserException(String message) { super(message); From 9fb9ad2aed885ad4164f6687a6d905b9e4e29a54 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 7 Oct 2021 19:07:26 +0800 Subject: [PATCH 072/607] Some formatting changes 1. Removed the dead line for Decodex 2. Took out the magic literals to be constants instead and some comments to explain what they are used for. --- src/main/java/decodex/Decodex.java | 1 - src/main/java/decodex/parser/Parser.java | 23 +++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 2d45bbad9d..b35f3b3408 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -1,6 +1,5 @@ package decodex; - import decodex.commands.Command; import decodex.commands.ExitCommand; import decodex.data.DataManager; diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 3af622dfa5..f3e400c684 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -14,6 +14,21 @@ */ public class Parser { + /** + * Specifies the index of the tokens where command can be found. + */ + private static final int COMMAND_INDEX = 0; + + /** + * Specifies the starting index where the arguments can be found. + */ + private static final int STARTING_ARGUMENTS_INDEX = 1; + + /** + * Specifies the valid length of the tokens and used to check validity of tokens. + */ + private static final int VALID_TOKENS_LENGTH = 1; + /** * Returns the type of command that user has entered. * @@ -27,12 +42,12 @@ public String getCommandType(String userInput) throws ParserException { String[] tokens = userInput.split(" "); - boolean isInvalidTokensLength = tokens.length < 1; + boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH; if (isInvalidTokensLength) { throw new ParserException(ParserException.WEIRD_COMMAND_TYPE_MESSAGE); } - return tokens[0]; + return tokens[COMMAND_INDEX]; } /** @@ -44,10 +59,10 @@ public String getCommandType(String userInput) throws ParserException { public String[] getUserArguments(String userInput) throws ParserException { String[] tokens = userInput.split(" "); - if (tokens.length <= 1) { + if (tokens.length <= VALID_TOKENS_LENGTH) { throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); } - return Arrays.copyOfRange(tokens, 1, tokens.length); + return Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); } /** From b651afb5903a36cd5be454d71f1cb9ea13e8c3f6 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 8 Oct 2021 01:07:36 +0800 Subject: [PATCH 073/607] Added stripping of trailing ends. --- src/main/java/decodex/parser/Parser.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index f3e400c684..a50e9d90d5 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -36,6 +36,7 @@ public class Parser { * @return The type of command. */ public String getCommandType(String userInput) throws ParserException { + userInput = userInput.strip(); if (userInput.isEmpty()) { throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); } From 719877392fb1b7389a4e81ab69c10021dac129d5 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 01:07:51 +0800 Subject: [PATCH 074/607] Add suggested changes --- src/main/java/decodex/Decodex.java | 2 +- src/test/java/decodex/commands/DataCommandTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index d4511d1217..b096e9f28f 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -43,7 +43,7 @@ public static void main(String[] args) { if (tokens.length < 1) { continue; } - String[] arguments = Arrays.copyOfRange(tokens,1, tokens.length); + String[] arguments = Arrays.copyOfRange(tokens, 1, tokens.length); switch (tokens[0]) { case DataCommand.COMMAND_WORD: diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 61c5d6e691..11f3c10460 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -40,4 +40,4 @@ public void run_insertData_insertedData() throws CommandException, DataManagerEx Data testData = new Data(dataString); assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } -} \ No newline at end of file +} From c5b1f6951f2fd628e6f9cf275c66eb03f0ee4eb4 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 8 Oct 2021 01:19:34 +0800 Subject: [PATCH 075/607] Changes to the CONSTANTS used for checking validity of the tokens. --- src/main/java/decodex/parser/Parser.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index a50e9d90d5..956b3e0766 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -27,7 +27,8 @@ public class Parser { /** * Specifies the valid length of the tokens and used to check validity of tokens. */ - private static final int VALID_TOKENS_LENGTH = 1; + private static final int VALID_TOKENS_LENGTH_FOR_COMMAND = 1; + private static final int VALID_TOKENS_LENGTH_FOR_ARGUMENTS = 2; /** * Returns the type of command that user has entered. @@ -43,7 +44,7 @@ public String getCommandType(String userInput) throws ParserException { String[] tokens = userInput.split(" "); - boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH; + boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH_FOR_COMMAND; if (isInvalidTokensLength) { throw new ParserException(ParserException.WEIRD_COMMAND_TYPE_MESSAGE); @@ -60,7 +61,7 @@ public String getCommandType(String userInput) throws ParserException { public String[] getUserArguments(String userInput) throws ParserException { String[] tokens = userInput.split(" "); - if (tokens.length <= VALID_TOKENS_LENGTH) { + if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); } return Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); From cd9b2606db9356759101971bdf4a589db7bdcd7d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 8 Oct 2021 01:40:28 +0800 Subject: [PATCH 076/607] Moved ParserException into decodex.data.exception package. --- src/main/java/decodex/Decodex.java | 2 +- .../decodex/{parser => data}/exception/ParserException.java | 2 +- src/main/java/decodex/parser/Parser.java | 2 +- src/test/java/decodex/parser/ParserTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/decodex/{parser => data}/exception/ParserException.java (95%) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index b35f3b3408..367ddc1edc 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -5,7 +5,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.parser.Parser; -import decodex.parser.exception.ParserException; +import decodex.data.exception.ParserException; import decodex.ui.Ui; import java.util.Scanner; diff --git a/src/main/java/decodex/parser/exception/ParserException.java b/src/main/java/decodex/data/exception/ParserException.java similarity index 95% rename from src/main/java/decodex/parser/exception/ParserException.java rename to src/main/java/decodex/data/exception/ParserException.java index ef15672059..f6a99b29f8 100644 --- a/src/main/java/decodex/parser/exception/ParserException.java +++ b/src/main/java/decodex/data/exception/ParserException.java @@ -1,4 +1,4 @@ -package decodex.parser.exception; +package decodex.data.exception; public class ParserException extends Exception { diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 956b3e0766..a3167b64f6 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -4,7 +4,7 @@ import decodex.commands.ExitCommand; import decodex.data.DataManager; import decodex.modules.ModuleManager; -import decodex.parser.exception.ParserException; +import decodex.data.exception.ParserException; import decodex.ui.Ui; import java.util.Arrays; diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index b19909ec23..9be186f843 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -4,7 +4,7 @@ import decodex.commands.ExitCommand; import decodex.data.DataManager; import decodex.modules.ModuleManager; -import decodex.parser.exception.ParserException; +import decodex.data.exception.ParserException; import decodex.ui.Ui; import org.junit.jupiter.api.Test; From 7ab3e420bd5d8d2ef80c73e4b842c9e930b01cf6 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 13:36:28 +0800 Subject: [PATCH 077/607] Revert changes on Decodex and text-ui-tests --- src/main/java/decodex/Decodex.java | 21 ++++--------------- .../java/decodex/commands/DataCommand.java | 2 +- text-ui-test/EXPECTED.TXT | 2 -- text-ui-test/input.txt | 2 -- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index b096e9f28f..3df5c58cf3 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -1,13 +1,11 @@ package decodex; import decodex.commands.Command; -import decodex.commands.DataCommand; import decodex.commands.ExitCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.ui.Ui; -import java.util.Arrays; import java.util.Scanner; public class Decodex { @@ -24,7 +22,7 @@ public class Decodex { /** * Decodex entry-point for the java.decodex.Decodex application. */ - public static void main(String[] args) { + public static void main(String[] args) throws CommandException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); @@ -39,16 +37,8 @@ public static void main(String[] args) { do { System.out.print("Decodex > "); String userInput = in.nextLine(); - String[] tokens = userInput.split(" "); - if (tokens.length < 1) { - continue; - } - String[] arguments = Arrays.copyOfRange(tokens, 1, tokens.length); - switch (tokens[0]) { - case DataCommand.COMMAND_WORD: - command = new DataCommand(dataManager, moduleManager, ui, arguments); - break; + switch (userInput) { case ExitCommand.COMMAND_WORD: command = new ExitCommand(dataManager, moduleManager, ui); break; @@ -57,11 +47,8 @@ public static void main(String[] args) { System.out.println(userInput); continue; } - try { - command.run(); - } catch (CommandException err) { - System.out.println(err.getMessage()); - } + + command.run(); } while (!(command instanceof ExitCommand)); diff --git a/src/main/java/decodex/commands/DataCommand.java b/src/main/java/decodex/commands/DataCommand.java index c92d6ae5ab..35030b7229 100644 --- a/src/main/java/decodex/commands/DataCommand.java +++ b/src/main/java/decodex/commands/DataCommand.java @@ -11,7 +11,7 @@ public class DataCommand extends Command { public static final String COMMAND_WORD = "data"; public static final int DATA_STRING_INDEX = 0; public static final int ARGUMENT_COUNT = 1; - private String[] arguments; + private final String[] arguments; public DataCommand(DataManager dataManager, ModuleManager moduleManager, Ui ui, String[] arguments) { super(dataManager, moduleManager, ui); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 78510359e8..4679efac27 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -8,7 +8,5 @@ Hello from Decodex > whoami Decodex > bring me thanos Decodex > hi -Decodex > [-] Missing arguments -Decodex > [+] Input: something Decodex > bye Decodex > Goodbye! \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 6ffa937c0d..4c09ae32bc 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,7 +1,5 @@ whoami bring me thanos hi -data -data something bye exit \ No newline at end of file From e2ef133a316b9cd8c0fbe3fee543590ccbdf9408 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 13:49:07 +0800 Subject: [PATCH 078/607] Add CommandException --- .../java/decodex/data/exception/CommandException.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/decodex/data/exception/CommandException.java diff --git a/src/main/java/decodex/data/exception/CommandException.java b/src/main/java/decodex/data/exception/CommandException.java new file mode 100644 index 0000000000..574a1c592b --- /dev/null +++ b/src/main/java/decodex/data/exception/CommandException.java @@ -0,0 +1,11 @@ +package decodex.data.exception; + +/** + * This exception is raised when an error during command execution occurs. + */ +public class CommandException extends Exception { + + public CommandException(String message) { + super(message); + } +} From 7c33920e5e5ff81409db78c5664066881c9fb604 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 13:57:49 +0800 Subject: [PATCH 079/607] Add ModuleException --- .../java/decodex/data/exception/ModuleException.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/decodex/data/exception/ModuleException.java diff --git a/src/main/java/decodex/data/exception/ModuleException.java b/src/main/java/decodex/data/exception/ModuleException.java new file mode 100644 index 0000000000..90e41dcd08 --- /dev/null +++ b/src/main/java/decodex/data/exception/ModuleException.java @@ -0,0 +1,11 @@ +package decodex.data.exception; + +/** + * This exception is raised when an error during module execution occurs. + */ +public class ModuleException extends Exception { + + public ModuleException(String message) { + super(message); + } +} From e5b1328181b397cad694f8382463c828d591f2c4 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 15:51:36 +0800 Subject: [PATCH 080/607] Redefine Command class --- src/main/java/decodex/Decodex.java | 4 ++-- src/main/java/decodex/commands/Command.java | 13 +++---------- src/main/java/decodex/commands/ExitCommand.java | 6 +++--- src/main/java/decodex/parser/Parser.java | 13 +++++-------- src/test/java/decodex/parser/ParserTest.java | 8 +------- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 367ddc1edc..56d6d88b8e 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -56,12 +56,12 @@ public static void main(String[] args) { String userInput = in.nextLine(); try { - command = parser.parseCommand(userInput, dataManager, moduleManager, ui); + command = parser.parseCommand(userInput); } catch (ParserException err) { printErrorMessage(err.getMessage()); continue; } - command.run(); + command.run(dataManager, moduleManager, ui); } while (!(command instanceof ExitCommand)); System.exit(0); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index 83fa1f0eee..dbd919dbaa 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -3,22 +3,15 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.ui.Ui; +import javax.swing.UIManager; /** * Command serves as a foundation for other commands to be built upon. */ public abstract class Command { - protected DataManager dataManager; - protected ModuleManager moduleManager; - protected Ui ui; - - public Command(DataManager dataManager, ModuleManager moduleManager, Ui ui) { - this.dataManager = dataManager; - this.moduleManager = moduleManager; - this.ui = ui; + public Command() { } - public abstract void run(); - + public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui); } diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java index 3fc722aa8d..1394ae4d40 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -8,12 +8,12 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public ExitCommand(DataManager dataManager, ModuleManager moduleManager, Ui ui) { - super(dataManager, moduleManager, ui); + public ExitCommand() { + super(); } @Override - public void run() { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { System.out.print("Goodbye!"); } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index a3167b64f6..9064261932 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -68,16 +68,13 @@ public String[] getUserArguments(String userInput) throws ParserException { } /** - * Parses the user input provided by user and returns its respective command. - * Returns a null object if the command is invalid. + * Parses the user input provided by user and returns its respective command. Returns a null object if the command + * is invalid. * - * @param userInput The user input specified by the user. - * @param dataManager The DataManager object - * @param moduleManager The ModuleManager object - * @param ui The Ui object + * @param userInput The user input specified by the user. * @return The respective Command object. */ - public Command parseCommand(String userInput, DataManager dataManager, ModuleManager moduleManager, Ui ui) + public Command parseCommand(String userInput) throws ParserException { Command command; @@ -85,7 +82,7 @@ public Command parseCommand(String userInput, DataManager dataManager, ModuleMan switch (commandType) { case ExitCommand.COMMAND_WORD: - command = new ExitCommand(dataManager, moduleManager, ui); + command = new ExitCommand(); break; default: throw new ParserException(userInput + " !To be replaced with invalid command message!"); diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 9be186f843..e19d57f3df 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -2,10 +2,7 @@ import decodex.commands.Command; import decodex.commands.ExitCommand; -import decodex.data.DataManager; -import decodex.modules.ModuleManager; import decodex.data.exception.ParserException; -import decodex.ui.Ui; import org.junit.jupiter.api.Test; @@ -15,10 +12,7 @@ class ParserTest { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); Parser parser = new Parser(); - Ui ui = new Ui(); @Test void getCommandType_stringWithNoSeparator_expectCommandTypeString() throws ParserException { @@ -54,7 +48,7 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserException { String userInput = "exit"; - Command command = parser.parseCommand(userInput, dataManager, moduleManager, ui); + Command command = parser.parseCommand(userInput); assertTrue(command instanceof ExitCommand); } From 4969651fedeff8e6a4b579e7b3c2a90755729327 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 15:56:42 +0800 Subject: [PATCH 081/607] Remove unintended auto-import --- src/main/java/decodex/commands/Command.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index dbd919dbaa..32eb16ff1f 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -3,7 +3,6 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.ui.Ui; -import javax.swing.UIManager; /** * Command serves as a foundation for other commands to be built upon. From f1556fb49a243e587b84c69935776d0136a4843f Mon Sep 17 00:00:00 2001 From: rizemon Date: Thu, 7 Oct 2021 11:51:41 +0800 Subject: [PATCH 082/607] Add DataCommand --- src/main/java/decodex/Decodex.java | 3 +- src/main/java/decodex/commands/Command.java | 3 +- .../java/decodex/commands/DataCommand.java | 31 +++++++++++++ .../decodex/commands/DataCommandTest.java | 43 +++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 src/main/java/decodex/commands/DataCommand.java create mode 100644 src/test/java/decodex/commands/DataCommandTest.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 56d6d88b8e..4c6a8af81a 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -3,6 +3,7 @@ import decodex.commands.Command; import decodex.commands.ExitCommand; import decodex.data.DataManager; +import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.parser.Parser; import decodex.data.exception.ParserException; @@ -45,7 +46,7 @@ public static void initDecodex() { /** * Decodex entry-point for the java.decodex.Decodex application. */ - public static void main(String[] args) { + public static void main(String[] args) throws CommandException { printGreeting(); initDecodex(); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index 32eb16ff1f..d55e57d2a6 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -1,6 +1,7 @@ package decodex.commands; import decodex.data.DataManager; +import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -12,5 +13,5 @@ public abstract class Command { public Command() { } - public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui); + public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws CommandException; } diff --git a/src/main/java/decodex/commands/DataCommand.java b/src/main/java/decodex/commands/DataCommand.java new file mode 100644 index 0000000000..fd72cf9ef0 --- /dev/null +++ b/src/main/java/decodex/commands/DataCommand.java @@ -0,0 +1,31 @@ +package decodex.commands; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +public class DataCommand extends Command { + + public static final String COMMAND_WORD = "data"; + public static final int DATA_STRING_INDEX = 0; + public static final int ARGUMENT_COUNT = 1; + private String[] arguments; + + public DataCommand(String[] arguments) { + super(); + this.arguments = arguments; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws CommandException { + if (arguments.length < ARGUMENT_COUNT) { + throw new CommandException("[-] Missing arguments"); + } + String dataString = arguments[DATA_STRING_INDEX]; + Data userData = new Data(dataString); + dataManager.setOriginalData(userData); + System.out.printf("[+] Input: %s\n", dataString); + } +} diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java new file mode 100644 index 0000000000..10abe08059 --- /dev/null +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -0,0 +1,43 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +public class DataCommandTest { + + @Test + public void run_missingArguments_expectException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + String[] testArguments = {}; + Command testCommand = new DataCommand(testArguments); + + assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui)); + } + + @Test + public void run_insertData_insertedData() throws CommandException, DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + String dataString = "something"; + String[] testArguments = {dataString}; + Command testCommand = new DataCommand(testArguments); + testCommand.run(dataManager, moduleManager, ui); + + Data testData = new Data(dataString); + assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); + } +} \ No newline at end of file From 82174b95bbc3062bbaf2373e05dc6ea57353f56e Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 01:07:51 +0800 Subject: [PATCH 083/607] Add suggested changes --- src/test/java/decodex/commands/DataCommandTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 10abe08059..4efff3fcd7 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -40,4 +40,4 @@ public void run_insertData_insertedData() throws CommandException, DataManagerEx Data testData = new Data(dataString); assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } -} \ No newline at end of file +} From cf4c6d9ec56b3a654039d7a067bf06043a91c853 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 13:36:28 +0800 Subject: [PATCH 084/607] Revert changes on Decodex and text-ui-tests --- src/main/java/decodex/commands/DataCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/commands/DataCommand.java b/src/main/java/decodex/commands/DataCommand.java index fd72cf9ef0..cd1ae2c168 100644 --- a/src/main/java/decodex/commands/DataCommand.java +++ b/src/main/java/decodex/commands/DataCommand.java @@ -11,7 +11,7 @@ public class DataCommand extends Command { public static final String COMMAND_WORD = "data"; public static final int DATA_STRING_INDEX = 0; public static final int ARGUMENT_COUNT = 1; - private String[] arguments; + private final String[] arguments; public DataCommand(String[] arguments) { super(); From f697d43e7d324d5e1695976f0b32b81bcba251a3 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 18:23:14 +0800 Subject: [PATCH 085/607] Move exception handling to try-catch --- src/main/java/decodex/Decodex.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 4c6a8af81a..722b7cfd1c 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -46,7 +46,7 @@ public static void initDecodex() { /** * Decodex entry-point for the java.decodex.Decodex application. */ - public static void main(String[] args) throws CommandException { + public static void main(String[] args) { printGreeting(); initDecodex(); @@ -58,11 +58,11 @@ public static void main(String[] args) throws CommandException { try { command = parser.parseCommand(userInput); - } catch (ParserException err) { + command.run(dataManager, moduleManager, ui); + } catch (ParserException | CommandException err) { printErrorMessage(err.getMessage()); continue; } - command.run(dataManager, moduleManager, ui); } while (!(command instanceof ExitCommand)); System.exit(0); From 9709bcdc79d8f8eb9e9080de6b934d20ab7aaa92 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 19:01:14 +0800 Subject: [PATCH 086/607] Replace argument with dataString --- src/main/java/decodex/commands/DataCommand.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/commands/DataCommand.java b/src/main/java/decodex/commands/DataCommand.java index a4d1cef7dd..64bc27d686 100644 --- a/src/main/java/decodex/commands/DataCommand.java +++ b/src/main/java/decodex/commands/DataCommand.java @@ -10,19 +10,18 @@ public class DataCommand extends Command { public static final String COMMAND_WORD = "data"; - private final String argument; + private final String dataString; - public DataCommand(String argument) { + public DataCommand(String dataString) { super(); - this.argument = argument; + this.dataString = dataString; } @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws CommandException { - if (argument.isEmpty()) { + if (dataString.isEmpty()) { throw new CommandException("[-] Missing argument"); } - String dataString = argument; Data userData = new Data(dataString); dataManager.setOriginalData(userData); System.out.printf("[+] Input: %s\n", dataString); From fafac86c6afe53bb473af2d411383dca670ef373 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 21:14:58 +0800 Subject: [PATCH 087/607] Add ResetCommand --- .../java/decodex/commands/ResetCommand.java | 20 ++++++++ .../decodex/commands/ResetCommandTest.java | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/decodex/commands/ResetCommand.java create mode 100644 src/test/java/decodex/commands/ResetCommandTest.java diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java new file mode 100644 index 0000000000..d84ded6a02 --- /dev/null +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -0,0 +1,20 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +public class ResetCommand extends Command { + + public static final String COMMAND_WORD = "reset"; + + public ResetCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + dataManager.resetToOriginalData(); + System.out.println("[+] Reverted all changes"); + } +} \ No newline at end of file diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java new file mode 100644 index 0000000000..7b09c71303 --- /dev/null +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -0,0 +1,46 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.DataManagerException; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +class ResetCommandTest { + + @Test + public void run_noChanges_sameOriginalData() throws DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + Data originalData = new Data("hi"); + dataManager.setOriginalData(originalData); + + Command testCommand = new ResetCommand(); + testCommand.run(dataManager, moduleManager, ui); + assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); + } + + @Test + public void run_newData_sameOriginalData() throws DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + Data originalData = new Data("hi"); + dataManager.setOriginalData(originalData); + Data newData = new Data("bye"); + dataManager.setCurrentData(newData); + + Command testCommand = new ResetCommand(); + testCommand.run(dataManager, moduleManager, ui); + assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); + } +} \ No newline at end of file From e81c66bcf7cdead2a3a5f6fa1f17c0691ed58692 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 8 Oct 2021 21:17:46 +0800 Subject: [PATCH 088/607] Add newline at end of file --- src/main/java/decodex/commands/ResetCommand.java | 2 +- src/test/java/decodex/commands/ResetCommandTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java index d84ded6a02..e6086ac90f 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -17,4 +17,4 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { dataManager.resetToOriginalData(); System.out.println("[+] Reverted all changes"); } -} \ No newline at end of file +} diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index 7b09c71303..f59f98455b 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -22,7 +22,7 @@ public void run_noChanges_sameOriginalData() throws DataManagerException { Data originalData = new Data("hi"); dataManager.setOriginalData(originalData); - + Command testCommand = new ResetCommand(); testCommand.run(dataManager, moduleManager, ui); assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); @@ -43,4 +43,4 @@ public void run_newData_sameOriginalData() throws DataManagerException { testCommand.run(dataManager, moduleManager, ui); assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); } -} \ No newline at end of file +} From bfb5a4d7d0205bb3e533958196a734f737a53f69 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 9 Oct 2021 00:24:41 +0800 Subject: [PATCH 089/607] Add requested changes --- src/main/java/decodex/parser/Parser.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 9064261932..db910e6845 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -74,8 +74,7 @@ public String[] getUserArguments(String userInput) throws ParserException { * @param userInput The user input specified by the user. * @return The respective Command object. */ - public Command parseCommand(String userInput) - throws ParserException { + public Command parseCommand(String userInput) throws ParserException { Command command; String commandType = getCommandType(userInput); From 53ad8b023310eb1bb9ff0e3fd17bd7c1c91fc3e6 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 9 Oct 2021 00:52:51 +0800 Subject: [PATCH 090/607] Add CommandException to tests --- src/test/java/decodex/commands/ResetCommandTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index f59f98455b..b0da9b9f4c 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -8,6 +8,7 @@ import decodex.data.Data; import decodex.data.DataManager; +import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -15,7 +16,7 @@ class ResetCommandTest { @Test - public void run_noChanges_sameOriginalData() throws DataManagerException { + public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); @@ -29,7 +30,7 @@ public void run_noChanges_sameOriginalData() throws DataManagerException { } @Test - public void run_newData_sameOriginalData() throws DataManagerException { + public void run_newData_sameOriginalData() throws CommandException, DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); From 491c59cf8063ea095724e2e55f9b3309f32a8f6e Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sat, 9 Oct 2021 19:09:29 +0800 Subject: [PATCH 091/607] Add SelectCommand * Add `SelectCommand` and `SelectCommandTest` * Add `UnknownModuleException` and `DataManagerException` to `Command`'s `run` method signature * Add `UnknownModuleException` to `DataCommandTest`'s and `ResetCommandTest`'s test method signature --- src/main/java/decodex/Decodex.java | 5 +- src/main/java/decodex/commands/Command.java | 5 +- .../java/decodex/commands/SelectCommand.java | 36 +++++++++++ .../decodex/commands/DataCommandTest.java | 3 +- .../decodex/commands/ResetCommandTest.java | 5 +- .../decodex/commands/SelectCommandTest.java | 62 +++++++++++++++++++ 6 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 src/main/java/decodex/commands/SelectCommand.java create mode 100644 src/test/java/decodex/commands/SelectCommandTest.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 722b7cfd1c..4e1e853175 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -4,6 +4,8 @@ import decodex.commands.ExitCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.parser.Parser; import decodex.data.exception.ParserException; @@ -59,9 +61,8 @@ public static void main(String[] args) { try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); - } catch (ParserException | CommandException err) { + } catch (ParserException | CommandException | UnknownModuleException | DataManagerException err) { printErrorMessage(err.getMessage()); - continue; } } while (!(command instanceof ExitCommand)); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index d55e57d2a6..aa3483faf4 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -2,6 +2,8 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -13,5 +15,6 @@ public abstract class Command { public Command() { } - public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws CommandException; + public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) + throws CommandException, UnknownModuleException, DataManagerException; } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java new file mode 100644 index 0000000000..fb3c1c1c18 --- /dev/null +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -0,0 +1,36 @@ +package decodex.commands; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.BaseModule; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +public class SelectCommand extends Command { + + public static final String COMMAND_WORD = "select"; + + private final String moduleName; + + public SelectCommand(String moduleName) { + super(); + this.moduleName = moduleName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) + throws UnknownModuleException, CommandException, DataManagerException { + if (moduleName.isBlank()) { + throw new CommandException("[-] Missing module name"); + } + + BaseModule module = moduleManager.selectModule(moduleName); + Data newData = module.run(dataManager.getCurrentData()); + dataManager.setCurrentData(newData); + + System.out.printf("[+] Output: %s\n", newData.toString()); + } +} diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 06b3e22a68..9ce9af1213 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -27,7 +28,7 @@ public void run_missingArguments_expectException() { } @Test - public void run_insertData_insertedData() throws CommandException, DataManagerException { + public void run_insertData_insertedData() throws CommandException, DataManagerException, UnknownModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index b0da9b9f4c..c13ee9fb5b 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; +import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -16,7 +17,7 @@ class ResetCommandTest { @Test - public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException { + public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException, UnknownModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); @@ -30,7 +31,7 @@ public void run_noChanges_sameOriginalData() throws CommandException, DataManage } @Test - public void run_newData_sameOriginalData() throws CommandException, DataManagerException { + public void run_newData_sameOriginalData() throws CommandException, DataManagerException, UnknownModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java new file mode 100644 index 0000000000..0bd59a7402 --- /dev/null +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -0,0 +1,62 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; +import org.junit.jupiter.api.Test; + +public class SelectCommandTest { + + @Test + public void run_validModuleName_success() throws UnknownModuleException, CommandException, DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + DataCommand dataCommand = new DataCommand("hello world"); + dataCommand.run(dataManager, moduleManager, ui); + + String moduleName = "base64encode"; + Command selectCommand = new SelectCommand(moduleName); + selectCommand.run(dataManager, moduleManager, ui); + + assertEquals("aGVsbG8gd29ybGQ=", dataManager.getCurrentData().toString()); + } + + @Test + public void run_blankModuleName_expectException() throws CommandException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + DataCommand dataCommand = new DataCommand("hello world"); + dataCommand.run(dataManager, moduleManager, ui); + + String moduleName = ""; + Command selectCommand = new SelectCommand(moduleName); + + assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); + } + + @Test + public void run_unknownModuleName_expectException() throws CommandException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + DataCommand dataCommand = new DataCommand("hello world"); + dataCommand.run(dataManager, moduleManager, ui); + + String moduleName = "unknownModule"; + Command selectCommand = new SelectCommand(moduleName); + + assertThrows(UnknownModuleException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); + } + +} From 23684386593023e4aac80aa791f3e42007edbb36 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sat, 9 Oct 2021 19:38:48 +0800 Subject: [PATCH 092/607] Add ListCommand --- .../java/decodex/commands/ListCommand.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/main/java/decodex/commands/ListCommand.java diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java new file mode 100644 index 0000000000..6f1e4b360a --- /dev/null +++ b/src/main/java/decodex/commands/ListCommand.java @@ -0,0 +1,41 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.modules.BaseModule; +import decodex.modules.ModuleManager; +import decodex.ui.Ui; + +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + public ListCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + BaseModule[] modules = moduleManager.getModules(); + + StringBuilder moduleListString = new StringBuilder(); + int maxNameWidth = 0; + + // Find number of characters of the longest module name + for (BaseModule module : modules) { + String moduleName = module.getName(); + if (moduleName.length() > maxNameWidth) { + maxNameWidth = moduleName.length(); + } + } + + // Prepare and format list of modules + for (BaseModule module : modules) { + String moduleName = module.getName(); + String moduleDescription = module.getDescription(); + moduleListString.append(String.format(" %-" + maxNameWidth + "s - %s\n", moduleName, moduleDescription)); + } + + System.out.println("Here are the list of supported modules:"); + System.out.println(moduleListString); + } +} From 47318e681d02929d6d3019a19dec572c9c61d954 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sat, 9 Oct 2021 19:46:35 +0800 Subject: [PATCH 093/607] Set data in `DataManager` instead of relying on `DataCommand` --- src/test/java/decodex/commands/SelectCommandTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index 0bd59a7402..d2d94e78db 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.data.Data; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; @@ -19,11 +20,11 @@ public void run_validModuleName_success() throws UnknownModuleException, Command ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - DataCommand dataCommand = new DataCommand("hello world"); - dataCommand.run(dataManager, moduleManager, ui); + Data data = new Data("hello world"); + dataManager.setOriginalData(data); String moduleName = "base64encode"; - Command selectCommand = new SelectCommand(moduleName); + SelectCommand selectCommand = new SelectCommand(moduleName); selectCommand.run(dataManager, moduleManager, ui); assertEquals("aGVsbG8gd29ybGQ=", dataManager.getCurrentData().toString()); From a87dfec876946200e03656ee283c0b173c8c64ac Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sat, 9 Oct 2021 19:47:44 +0800 Subject: [PATCH 094/607] Use `SelectCommand` instead of `Command` --- src/test/java/decodex/commands/SelectCommandTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index d2d94e78db..26ecc4c04e 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -40,7 +40,7 @@ public void run_blankModuleName_expectException() throws CommandException { dataCommand.run(dataManager, moduleManager, ui); String moduleName = ""; - Command selectCommand = new SelectCommand(moduleName); + SelectCommand selectCommand = new SelectCommand(moduleName); assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); } @@ -55,7 +55,7 @@ public void run_unknownModuleName_expectException() throws CommandException { dataCommand.run(dataManager, moduleManager, ui); String moduleName = "unknownModule"; - Command selectCommand = new SelectCommand(moduleName); + SelectCommand selectCommand = new SelectCommand(moduleName); assertThrows(UnknownModuleException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); } From 9c8dd56396478a4d4bf117472f737d4293e56d21 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sat, 9 Oct 2021 19:57:41 +0800 Subject: [PATCH 095/607] Change remaining setting of data in `DataManager` instead of relying on `DataCommand` --- src/test/java/decodex/commands/SelectCommandTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index 26ecc4c04e..c8e62a4d24 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -36,8 +36,8 @@ public void run_blankModuleName_expectException() throws CommandException { ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - DataCommand dataCommand = new DataCommand("hello world"); - dataCommand.run(dataManager, moduleManager, ui); + Data data = new Data("hello world"); + dataManager.setOriginalData(data); String moduleName = ""; SelectCommand selectCommand = new SelectCommand(moduleName); @@ -51,8 +51,8 @@ public void run_unknownModuleName_expectException() throws CommandException { ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - DataCommand dataCommand = new DataCommand("hello world"); - dataCommand.run(dataManager, moduleManager, ui); + Data data = new Data("hello world"); + dataManager.setOriginalData(data); String moduleName = "unknownModule"; SelectCommand selectCommand = new SelectCommand(moduleName); From d67fb6af2866ba68d77719a40e6aca188c8aa6eb Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 17:56:20 +0800 Subject: [PATCH 096/607] Removed unused imports for Parser. --- src/main/java/decodex/parser/Parser.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index db910e6845..c029f15c73 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -2,10 +2,7 @@ import decodex.commands.Command; import decodex.commands.ExitCommand; -import decodex.data.DataManager; -import decodex.modules.ModuleManager; import decodex.data.exception.ParserException; -import decodex.ui.Ui; import java.util.Arrays; From 7de709b216ecd4f985668d99f90a1f3cc8162524 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 18:35:42 +0800 Subject: [PATCH 097/607] Added more switch cases to run the respective commands. --- src/main/java/decodex/parser/Parser.java | 106 +++++++++++++++++-- src/test/java/decodex/parser/ParserTest.java | 2 +- 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index c029f15c73..2268374115 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -1,10 +1,15 @@ package decodex.parser; import decodex.commands.Command; +import decodex.commands.DataCommand; import decodex.commands.ExitCommand; +import decodex.commands.ListCommand; +import decodex.commands.ResetCommand; +import decodex.commands.SelectCommand; import decodex.data.exception.ParserException; import java.util.Arrays; +import java.util.List; /** * Parses and validates the user input. @@ -12,10 +17,11 @@ public class Parser { /** - * Specifies the index of the tokens where command can be found. + * Specifies the index of the tokens where specific parameters can be found. */ private static final int COMMAND_INDEX = 0; + /** * Specifies the starting index where the arguments can be found. */ @@ -50,12 +56,13 @@ public String getCommandType(String userInput) throws ParserException { } /** - * Returns the Argument portion of the user input. + * Returns the Argument portion of the user input as an array. + * NOTE - Might be removed since DataCommand takes in only String rather than array. * * @param userInput The input specified by the user. - * @return The argument portion of the user input. + * @return The argument portion of the user input as an array. */ - public String[] getUserArguments(String userInput) throws ParserException { + public String[] getUserArgumentsAsArray(String userInput) throws ParserException { String[] tokens = userInput.split(" "); if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { @@ -64,6 +71,28 @@ public String[] getUserArguments(String userInput) throws ParserException { return Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); } + /** + * Returns the Argument portion of the user input as a whole string. + * + * @param userInput The input specified by the user. + * @return The argument portion of the user input as an array. + */ + public String getUserArgumentsAsString(String userInput) throws ParserException { + String[] tokens = userInput.split(" "); + + if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { + throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); + } + String[] argumentTokens = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); + + if (argumentTokens.length > 1) { + String joinedArguments = String.join(" ", argumentTokens); + return joinedArguments; + } + String singleArgument = argumentTokens[0]; + return singleArgument; + } + /** * Parses the user input provided by user and returns its respective command. Returns a null object if the command * is invalid. @@ -73,16 +102,79 @@ public String[] getUserArguments(String userInput) throws ParserException { */ public Command parseCommand(String userInput) throws ParserException { Command command; - String commandType = getCommandType(userInput); switch (commandType) { case ExitCommand.COMMAND_WORD: - command = new ExitCommand(); + command = craftExitCommand(); + break; + case DataCommand.COMMAND_WORD: + command = craftDataCommand(userInput); + break; + case ListCommand.COMMAND_WORD: + command = craftListCommand(); + break; + case ResetCommand.COMMAND_WORD: + command = craftResetCommand(); + break; + case SelectCommand.COMMAND_WORD: + command = craftSelectCommand(userInput); break; default: - throw new ParserException(userInput + " !To be replaced with invalid command message!"); + throw new ParserException("[-] Unknown command, please enter a valid command"); } return command; } + + /** + * Crafts and returns the Exit Command. + * + * @return The Exit command. + */ + private Command craftExitCommand () { + return new ExitCommand(); + } + + /** + * Crafts and returns the Data Command given the user input. + * + * @param userInput The user input specified by the user. + * @return The Data command + * @throws ParserException + */ + private Command craftDataCommand(String userInput) throws ParserException { + String arguments = getUserArgumentsAsString(userInput); + return new DataCommand(arguments); + } + + /** + * Crafts and returns the List Command. + * + * @return The List command. + */ + private Command craftListCommand() { + return new ListCommand(); + } + + /** + * Crafts and returns the Reset Command. + * + * @return The Reset command. + */ + private Command craftResetCommand() { + return new ResetCommand(); + } + + /** + * Crafts and returns the Select Command. + * + * @param userInput The user input specified by the user. + * @return The Select command. + * @throws ParserException + */ + private Command craftSelectCommand(String userInput) throws ParserException { + String moduleName = getUserArgumentsAsString(userInput); + return new SelectCommand(moduleName); + } + } diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index e19d57f3df..a3bfdbcf8a 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -39,7 +39,7 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() String testInput = "test data"; String[] expectedOutput = new String[]{"data"}; - String[] arguments = parser.getUserArguments(testInput); + String[] arguments = parser.getUserArgumentsAsArray(testInput); assertArrayEquals(expectedOutput, arguments); } From e26950491c69f07b0249a241f023948d813e510e Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 18:41:11 +0800 Subject: [PATCH 098/607] Changes the Base64 Encoding description. --- src/main/java/decodex/modules/base64/Base64Encoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/modules/base64/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java index 19e520d771..1afd5ca670 100644 --- a/src/main/java/decodex/modules/base64/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -13,7 +13,7 @@ public class Base64Encoder extends BaseModule { private final Base64.Encoder base64Encoder; public static final String MODULE_NAME = "base64encode"; - public static final String MODULE_DESCRIPTION = "Base64-encodes the data"; + public static final String MODULE_DESCRIPTION = "Encodes the data using Base64 format."; public Base64Encoder() { super(MODULE_NAME, MODULE_DESCRIPTION); From 1e405f4ae6ca6ae2c84d953bff25a6ea552d47c4 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 18:46:07 +0800 Subject: [PATCH 099/607] Changes to Error Messages for ParserExceptions --- src/main/java/decodex/data/exception/ParserException.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/data/exception/ParserException.java b/src/main/java/decodex/data/exception/ParserException.java index f6a99b29f8..bc7bdc09b3 100644 --- a/src/main/java/decodex/data/exception/ParserException.java +++ b/src/main/java/decodex/data/exception/ParserException.java @@ -2,14 +2,13 @@ public class ParserException extends Exception { - public static final String MISSING_COMMAND_ARGS_MESSAGE = "Your command arguments is empty." - + " Please try again after tone...BEEP"; - public static final String MISSING_COMMAND_TYPE_MESSAGE = "Command cannot be empty!!"; + public static final String MISSING_COMMAND_ARGS_MESSAGE = "[-] Your command arguments is empty"; + public static final String MISSING_COMMAND_TYPE_MESSAGE = "[-] Command cannot be empty"; /** * This error message is dedicated to users intending to find bugs in our program. */ - public static final String WEIRD_COMMAND_TYPE_MESSAGE = "In what universe did you think that would break us." + public static final String WEIRD_COMMAND_TYPE_MESSAGE = "[x] In what universe did you think that would break us." + " Please try again and use the valid commands, thanks."; public ParserException(String message) { From 54c54befb1c7eb94f36a149e637316ee272b90d7 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 18:55:54 +0800 Subject: [PATCH 100/607] Error handling for Base64 Decoding - where string given is not a valid base64 string. --- .../decodex/data/exception/ModuleException.java | 3 +++ src/main/java/decodex/modules/BaseModule.java | 3 ++- .../decodex/modules/base64/Base64Decoder.java | 17 +++++++++++------ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/decodex/data/exception/ModuleException.java b/src/main/java/decodex/data/exception/ModuleException.java index 90e41dcd08..b391ac5e85 100644 --- a/src/main/java/decodex/data/exception/ModuleException.java +++ b/src/main/java/decodex/data/exception/ModuleException.java @@ -5,6 +5,9 @@ */ public class ModuleException extends Exception { + public static final String BASE64_DECODING_FAILED_MESSAGE = "[x] " + + "This data does not seem to be in Base64 format"; + public ModuleException(String message) { super(message); } diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/BaseModule.java index 6292f45c33..c3a8552861 100644 --- a/src/main/java/decodex/modules/BaseModule.java +++ b/src/main/java/decodex/modules/BaseModule.java @@ -1,6 +1,7 @@ package decodex.modules; import decodex.data.Data; +import decodex.data.exception.ModuleException; /** * BaseModule serves as a foundation for other modules to be built upon. @@ -28,5 +29,5 @@ public String getDescription() { * * @return Data object containing new processed data. */ - public abstract Data run(Data data); + public abstract Data run(Data data) throws ModuleException; } diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 5733578325..1bf64f5ab8 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -1,5 +1,6 @@ package decodex.modules.base64; +import decodex.data.exception.ModuleException; import decodex.modules.BaseModule; import java.util.Base64; @@ -27,11 +28,15 @@ public Base64Decoder() { * @return The Data object of the Base64-decoded data. */ @Override - public Data run(Data data) { - Data decodedData; - byte[] originalBytes = data.getRawBytes(); - byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); - decodedData = new Data(base64DecodedBytes); - return decodedData; + public Data run(Data data) throws ModuleException { + try { + Data decodedData; + byte[] originalBytes = data.getRawBytes(); + byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); + decodedData = new Data(base64DecodedBytes); + return decodedData; + } catch (IllegalArgumentException err) { + throw new ModuleException(ModuleException.BASE64_DECODING_FAILED_MESSAGE); + } } } From fb565eaecba35904985915cc71718fa73c151a86 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 18:56:34 +0800 Subject: [PATCH 101/607] Reorganized Decodex.java to have a run method instead. --- src/main/java/decodex/Decodex.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 4e1e853175..e0c88ac418 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -49,6 +49,10 @@ public static void initDecodex() { * Decodex entry-point for the java.decodex.Decodex application. */ public static void main(String[] args) { + new Decodex().run(); + } + + public void run() { printGreeting(); initDecodex(); From 27e707e5dfc24ec07c3e87ab100975d060a0dc79 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 18:58:24 +0800 Subject: [PATCH 102/607] Reverted error handling for base64 decoding, requires discussion due to changes in other parts of the code in terms of error handling hierarchy. --- src/main/java/decodex/modules/BaseModule.java | 3 +-- .../decodex/modules/base64/Base64Decoder.java | 17 ++++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/BaseModule.java index c3a8552861..6292f45c33 100644 --- a/src/main/java/decodex/modules/BaseModule.java +++ b/src/main/java/decodex/modules/BaseModule.java @@ -1,7 +1,6 @@ package decodex.modules; import decodex.data.Data; -import decodex.data.exception.ModuleException; /** * BaseModule serves as a foundation for other modules to be built upon. @@ -29,5 +28,5 @@ public String getDescription() { * * @return Data object containing new processed data. */ - public abstract Data run(Data data) throws ModuleException; + public abstract Data run(Data data); } diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 1bf64f5ab8..5733578325 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -1,6 +1,5 @@ package decodex.modules.base64; -import decodex.data.exception.ModuleException; import decodex.modules.BaseModule; import java.util.Base64; @@ -28,15 +27,11 @@ public Base64Decoder() { * @return The Data object of the Base64-decoded data. */ @Override - public Data run(Data data) throws ModuleException { - try { - Data decodedData; - byte[] originalBytes = data.getRawBytes(); - byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); - decodedData = new Data(base64DecodedBytes); - return decodedData; - } catch (IllegalArgumentException err) { - throw new ModuleException(ModuleException.BASE64_DECODING_FAILED_MESSAGE); - } + public Data run(Data data) { + Data decodedData; + byte[] originalBytes = data.getRawBytes(); + byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); + decodedData = new Data(base64DecodedBytes); + return decodedData; } } From 8dabd942f5a2a2191cd8c240f8470185a160817c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 19:00:14 +0800 Subject: [PATCH 103/607] Reorganized some of the imports. --- src/main/java/decodex/modules/base64/Base64Decoder.java | 4 +++- src/main/java/decodex/parser/Parser.java | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 5733578325..813ab02707 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -1,9 +1,11 @@ package decodex.modules.base64; +import decodex.data.Data; import decodex.modules.BaseModule; + import java.util.Base64; -import decodex.data.Data; + /** * Base64Encoder serves to carry out the Base64 decoding operations. diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 2268374115..c1c32a6906 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -9,7 +9,6 @@ import decodex.data.exception.ParserException; import java.util.Arrays; -import java.util.List; /** * Parses and validates the user input. From 62fc49740a2f7dee68bb09ccb4cd3728941ccd70 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 19:04:54 +0800 Subject: [PATCH 104/607] Updated Text-Ui-Test --- text-ui-test/EXPECTED.TXT | 12 ++++++++---- text-ui-test/input.txt | 6 +++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index a10b321cad..92330fa3b4 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,8 +5,12 @@ Hello from | |_| | |_| | < __/ |____/ \__,_|_|\_\___| -Decodex > whoami !To be replaced with invalid command message! -Decodex > bring me thanos !To be replaced with invalid command message! -Decodex > hi !To be replaced with invalid command message! -Decodex > bye !To be replaced with invalid command message! +Decodex > [-] Unknown command, please enter a valid command +Decodex > [-] Unknown command, please enter a valid command +Decodex > [+] Input: I am a lab rat +Decodex > [+] Output: SSBhbSBhIGxhYiByYXQ= +Decodex > [+] Output: I am a lab rat +Decodex > [+] Output: 4920616d2061206c616220726174 +Decodex > [+] Reverted all changes +Decodex > [-] Unknown command, please enter a valid command Decodex > Goodbye! \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 4c09ae32bc..eec04f7583 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,5 +1,9 @@ whoami bring me thanos -hi +data I am a lab rat +select base64encode +select base64decode +select hexencode +reset bye exit \ No newline at end of file From 2ee0fa057866a9620697898425b0eac39f0612a9 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 19:22:03 +0800 Subject: [PATCH 105/607] Added JUnit test for the regular workflow for the commands (switch-case logic). --- src/test/java/decodex/parser/ParserTest.java | 43 +++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index a3bfdbcf8a..96dab70269 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -1,7 +1,6 @@ package decodex.parser; -import decodex.commands.Command; -import decodex.commands.ExitCommand; +import decodex.commands.*; import decodex.data.exception.ParserException; import org.junit.jupiter.api.Test; @@ -10,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; + class ParserTest { Parser parser = new Parser(); @@ -44,6 +44,18 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() assertArrayEquals(expectedOutput, arguments); } + + /* The JUnit test methods below are for testing "normal" usage flows for the respective commands.*/ + @Test + void parseCommand_userInputSpecifyingValidData_expectDataCommand() throws ParserException { + String userInput = "data dummyData"; + + Command command = parser.parseCommand(userInput); + + assertTrue(command instanceof DataCommand); + } + + @Test void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserException { String userInput = "exit"; @@ -52,4 +64,31 @@ void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserExcep assertTrue(command instanceof ExitCommand); } + + @Test + void parseCommand_userInputSpecifyingValidData_expectListCommand() throws ParserException { + String userInput = "list"; + + Command command = parser.parseCommand(userInput); + + assertTrue(command instanceof ListCommand); + } + + @Test + void parseCommand_userInputSpecifyingReset_expectResetCommand() throws ParserException { + String userInput = "reset"; + + Command command = parser.parseCommand(userInput); + + assertTrue(command instanceof ResetCommand); + } + + @Test + void parseCommand_userInputSpecifyingValidSelect_expectSelectCommand() throws ParserException { + String userInput = "select dummyMod"; + + Command command = parser.parseCommand(userInput); + + assertTrue(command instanceof SelectCommand); + } } From f1b885682fb7f3c527061d0f583fba8a51cc8e3f Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 19:27:01 +0800 Subject: [PATCH 106/607] Reorganized some of the Base64 imports. --- src/main/java/decodex/modules/base64/Base64Decoder.java | 2 -- src/main/java/decodex/modules/base64/Base64Encoder.java | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 813ab02707..115115e3a3 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -5,8 +5,6 @@ import java.util.Base64; - - /** * Base64Encoder serves to carry out the Base64 decoding operations. */ diff --git a/src/main/java/decodex/modules/base64/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java index 1afd5ca670..aa0b5641d0 100644 --- a/src/main/java/decodex/modules/base64/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -1,9 +1,9 @@ package decodex.modules.base64; +import decodex.data.Data; import decodex.modules.BaseModule; -import java.util.Base64; -import decodex.data.Data; +import java.util.Base64; /** * Base64Encoder serves to carry out the Base64 encoding operations. From ee5ad2d970cc4025399a5403fe7281054c2f74b0 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 19:27:45 +0800 Subject: [PATCH 107/607] Moved the Initialization and greeting calls the constructor instead. The printing of the greeting will be moved to UI in upcoming iterations. --- src/main/java/decodex/Decodex.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index e0c88ac418..17b8b3b5cd 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -34,6 +34,11 @@ public class Decodex { + "| |_| | |_| | < __/\n" + "|____/ \\__,_|_|\\_\\___|\n"; + public Decodex() { + printGreeting(); + initDecodex(); + } + /** * Initializes the necessary Objects for Decodex. */ @@ -53,9 +58,6 @@ public static void main(String[] args) { } public void run() { - printGreeting(); - initDecodex(); - Command command = null; do { From f360e2c6eb3d94fb7407462bf0e60e4361848e89 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 19:59:38 +0800 Subject: [PATCH 108/607] Fixed some of the checkstyle test issues. --- src/main/java/decodex/parser/Parser.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index c1c32a6906..564a19b66b 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -130,7 +130,7 @@ public Command parseCommand(String userInput) throws ParserException { * * @return The Exit command. */ - private Command craftExitCommand () { + private Command craftExitCommand() { return new ExitCommand(); } @@ -139,7 +139,7 @@ private Command craftExitCommand () { * * @param userInput The user input specified by the user. * @return The Data command - * @throws ParserException + * @throws ParserException ParserException */ private Command craftDataCommand(String userInput) throws ParserException { String arguments = getUserArgumentsAsString(userInput); @@ -169,7 +169,7 @@ private Command craftResetCommand() { * * @param userInput The user input specified by the user. * @return The Select command. - * @throws ParserException + * @throws ParserException ParserException */ private Command craftSelectCommand(String userInput) throws ParserException { String moduleName = getUserArgumentsAsString(userInput); From e94184b98b2bb349d71cce292b02863f0484a284 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 10 Oct 2021 20:01:29 +0800 Subject: [PATCH 109/607] Removed the auto * imports and replaced with the explicit imports. --- src/test/java/decodex/parser/ParserTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 96dab70269..d6625ef777 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -1,6 +1,11 @@ package decodex.parser; -import decodex.commands.*; +import decodex.commands.Command; +import decodex.commands.DataCommand; +import decodex.commands.ExitCommand; +import decodex.commands.ListCommand; +import decodex.commands.ResetCommand; +import decodex.commands.SelectCommand; import decodex.data.exception.ParserException; import org.junit.jupiter.api.Test; From 5d622fb2c96d01633700457d56f6e795c4fc9a69 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 10 Oct 2021 23:55:59 +0800 Subject: [PATCH 110/607] Update tests for `ModuleManager` * Reduce success test to selecting one module * Use `assertThrows` to detect exception thrown * Add test for blank module name --- .../decodex/modules/ModuleManagerTest.java | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index dd39f87061..e4cc940adb 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -1,26 +1,22 @@ package decodex.modules; -import decodex.data.exception.UnknownModuleException; -import decodex.modules.base64.Base64Decoder; -import decodex.modules.base64.Base64Encoder; -import decodex.modules.hex.HexDecoder; -import decodex.modules.hex.HexEncoder; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; + +import decodex.data.exception.UnknownModuleException; +import decodex.modules.base64.Base64Encoder; import org.junit.jupiter.api.Test; public class ModuleManagerTest { @Test - public void selectModule_availableModuleName_success() { + public void selectModule_base64encode_success() { + String moduleName = "base64encode"; ModuleManager moduleManager = new ModuleManager(); try { - assertTrue(moduleManager.selectModule("base64encode") instanceof Base64Encoder); - assertTrue(moduleManager.selectModule("base64decode") instanceof Base64Decoder); - assertTrue(moduleManager.selectModule("hexencode") instanceof HexEncoder); - assertTrue(moduleManager.selectModule("hexdecode") instanceof HexDecoder); + BaseModule module = moduleManager.selectModule(moduleName); + assertTrue(module instanceof Base64Encoder); } catch (UnknownModuleException e) { fail(); } @@ -28,12 +24,15 @@ public void selectModule_availableModuleName_success() { @Test public void selectModule_unknownModuleName_unknownModuleExceptionThrown() { + String moduleName = "unknownModule"; ModuleManager moduleManager = new ModuleManager(); - try { - moduleManager.selectModule("unknownModule"); - fail(); - } catch (UnknownModuleException e) { - assertEquals(UnknownModuleException.EXCEPTION_MESSAGE, e.getMessage()); - } + assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName)); + } + + @Test + public void selectModule_blankModuleName_unknownModuleExceptionThrown() { + String moduleName = ""; + ModuleManager moduleManager = new ModuleManager(); + assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName)); } } From 6e42b9801b0e9493ee5f2619bed8db29d31bc896 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 11:19:24 +0800 Subject: [PATCH 111/607] Replace try-catch in code to throwing exception in method signature --- src/test/java/decodex/modules/ModuleManagerTest.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index e4cc940adb..c50e1e7920 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -11,15 +11,12 @@ public class ModuleManagerTest { @Test - public void selectModule_base64encode_success() { + public void selectModule_base64encode_success() throws UnknownModuleException { String moduleName = "base64encode"; ModuleManager moduleManager = new ModuleManager(); - try { - BaseModule module = moduleManager.selectModule(moduleName); - assertTrue(module instanceof Base64Encoder); - } catch (UnknownModuleException e) { - fail(); - } + + BaseModule module = moduleManager.selectModule(moduleName); + assertTrue(module instanceof Base64Encoder); } @Test From 6a119cf16d7d5cf953d98f3547bebf1fc1ccb3a5 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 12:36:27 +0800 Subject: [PATCH 112/607] Removed The unnecessary method and it's corresponding JUnit test method. --- src/main/java/decodex/parser/Parser.java | 16 ---------------- src/test/java/decodex/parser/ParserTest.java | 12 ------------ 2 files changed, 28 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 564a19b66b..c0bbba9408 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -54,22 +54,6 @@ public String getCommandType(String userInput) throws ParserException { return tokens[COMMAND_INDEX]; } - /** - * Returns the Argument portion of the user input as an array. - * NOTE - Might be removed since DataCommand takes in only String rather than array. - * - * @param userInput The input specified by the user. - * @return The argument portion of the user input as an array. - */ - public String[] getUserArgumentsAsArray(String userInput) throws ParserException { - String[] tokens = userInput.split(" "); - - if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { - throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); - } - return Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); - } - /** * Returns the Argument portion of the user input as a whole string. * diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index d6625ef777..eba0f95faf 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -39,17 +38,6 @@ void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() throws assertEquals(expectedOutput, commandType); } - @Test - void getUserArguments_userInputWithOneSpaceSeparator_expectArrayOfOneArgument() throws ParserException { - String testInput = "test data"; - String[] expectedOutput = new String[]{"data"}; - - String[] arguments = parser.getUserArgumentsAsArray(testInput); - - assertArrayEquals(expectedOutput, arguments); - } - - /* The JUnit test methods below are for testing "normal" usage flows for the respective commands.*/ @Test void parseCommand_userInputSpecifyingValidData_expectDataCommand() throws ParserException { From 95196875b2f24cac7e3f2d0017acdd4e90a70bd1 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 12:45:06 +0800 Subject: [PATCH 113/607] Added the '-1' to split to ensure that double spacings will be kept in the joined string. Also, changed the weird message to a more meaningful name. --- src/main/java/decodex/data/exception/ParserException.java | 8 ++------ src/main/java/decodex/parser/Parser.java | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/data/exception/ParserException.java b/src/main/java/decodex/data/exception/ParserException.java index bc7bdc09b3..c767253d5a 100644 --- a/src/main/java/decodex/data/exception/ParserException.java +++ b/src/main/java/decodex/data/exception/ParserException.java @@ -2,15 +2,11 @@ public class ParserException extends Exception { + public static final String INVALID_TOKENS_LENGTH = "[x] In what universe did you think that would break us." + + " Please try again and use the valid commands, thanks."; public static final String MISSING_COMMAND_ARGS_MESSAGE = "[-] Your command arguments is empty"; public static final String MISSING_COMMAND_TYPE_MESSAGE = "[-] Command cannot be empty"; - /** - * This error message is dedicated to users intending to find bugs in our program. - */ - public static final String WEIRD_COMMAND_TYPE_MESSAGE = "[x] In what universe did you think that would break us." - + " Please try again and use the valid commands, thanks."; - public ParserException(String message) { super(message); } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index c0bbba9408..312454d58b 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -49,7 +49,7 @@ public String getCommandType(String userInput) throws ParserException { boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH_FOR_COMMAND; if (isInvalidTokensLength) { - throw new ParserException(ParserException.WEIRD_COMMAND_TYPE_MESSAGE); + throw new ParserException(ParserException.INVALID_TOKENS_LENGTH); } return tokens[COMMAND_INDEX]; } @@ -61,7 +61,7 @@ public String getCommandType(String userInput) throws ParserException { * @return The argument portion of the user input as an array. */ public String getUserArgumentsAsString(String userInput) throws ParserException { - String[] tokens = userInput.split(" "); + String[] tokens = userInput.split(" ", -1); if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); From fc4bfff8ae98303d633b11e092fd6c0ac96170af Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 12:49:07 +0800 Subject: [PATCH 114/607] Removed the additional error message which happens when tokens length is invalid for command. --- src/main/java/decodex/data/exception/ParserException.java | 6 ++---- src/main/java/decodex/parser/Parser.java | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/data/exception/ParserException.java b/src/main/java/decodex/data/exception/ParserException.java index c767253d5a..27c918f90a 100644 --- a/src/main/java/decodex/data/exception/ParserException.java +++ b/src/main/java/decodex/data/exception/ParserException.java @@ -1,11 +1,9 @@ package decodex.data.exception; public class ParserException extends Exception { - - public static final String INVALID_TOKENS_LENGTH = "[x] In what universe did you think that would break us." - + " Please try again and use the valid commands, thanks."; + public static final String MISSING_COMMAND_ARGS_MESSAGE = "[-] Your command arguments is empty"; - public static final String MISSING_COMMAND_TYPE_MESSAGE = "[-] Command cannot be empty"; + public static final String MISSING_COMMAND_TYPE_MESSAGE = "[-] Your command cannot be blank"; public ParserException(String message) { super(message); diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 312454d58b..e436e99f31 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -49,7 +49,7 @@ public String getCommandType(String userInput) throws ParserException { boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH_FOR_COMMAND; if (isInvalidTokensLength) { - throw new ParserException(ParserException.INVALID_TOKENS_LENGTH); + throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); } return tokens[COMMAND_INDEX]; } From 9d9f87288ee53f9810c34f6a8d269586abb71307 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 12:51:05 +0800 Subject: [PATCH 115/607] Changed the simple JUnit test to be for getUserArgumentsAsString. --- .../java/decodex/data/exception/ParserException.java | 2 +- src/test/java/decodex/parser/ParserTest.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/data/exception/ParserException.java b/src/main/java/decodex/data/exception/ParserException.java index 27c918f90a..5330f927bc 100644 --- a/src/main/java/decodex/data/exception/ParserException.java +++ b/src/main/java/decodex/data/exception/ParserException.java @@ -1,7 +1,7 @@ package decodex.data.exception; public class ParserException extends Exception { - + public static final String MISSING_COMMAND_ARGS_MESSAGE = "[-] Your command arguments is empty"; public static final String MISSING_COMMAND_TYPE_MESSAGE = "[-] Your command cannot be blank"; diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index eba0f95faf..c38041d757 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -13,7 +13,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; - class ParserTest { Parser parser = new Parser(); @@ -38,6 +37,17 @@ void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() throws assertEquals(expectedOutput, commandType); } + @Test + void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() throws ParserException { + String testInput = "test data"; + String expectedOutput = "data"; + + String arguments = parser.getUserArgumentsAsString(testInput); + + assertEquals(expectedOutput, arguments); + } + + /* The JUnit test methods below are for testing "normal" usage flows for the respective commands.*/ @Test void parseCommand_userInputSpecifyingValidData_expectDataCommand() throws ParserException { From 72a317b65cb64a6aed9b6dab29a0ac4039ab511d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 12:51:56 +0800 Subject: [PATCH 116/607] Renamed the method name for the getting of user arguments. --- src/main/java/decodex/parser/Parser.java | 6 +++--- src/test/java/decodex/parser/ParserTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index e436e99f31..1f7fa874f4 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -60,7 +60,7 @@ public String getCommandType(String userInput) throws ParserException { * @param userInput The input specified by the user. * @return The argument portion of the user input as an array. */ - public String getUserArgumentsAsString(String userInput) throws ParserException { + public String getUserArguments(String userInput) throws ParserException { String[] tokens = userInput.split(" ", -1); if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { @@ -126,7 +126,7 @@ private Command craftExitCommand() { * @throws ParserException ParserException */ private Command craftDataCommand(String userInput) throws ParserException { - String arguments = getUserArgumentsAsString(userInput); + String arguments = getUserArguments(userInput); return new DataCommand(arguments); } @@ -156,7 +156,7 @@ private Command craftResetCommand() { * @throws ParserException ParserException */ private Command craftSelectCommand(String userInput) throws ParserException { - String moduleName = getUserArgumentsAsString(userInput); + String moduleName = getUserArguments(userInput); return new SelectCommand(moduleName); } diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index c38041d757..3b6b01df32 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -42,7 +42,7 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() t String testInput = "test data"; String expectedOutput = "data"; - String arguments = parser.getUserArgumentsAsString(testInput); + String arguments = parser.getUserArguments(testInput); assertEquals(expectedOutput, arguments); } From ef9b15bd941b6493d13ba846a19d9cc36c17a5f4 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:15:17 +0800 Subject: [PATCH 117/607] Error handling for base64decode - Sample error message. --- src/main/java/decodex/Decodex.java | 3 ++- src/main/java/decodex/commands/Command.java | 3 ++- .../java/decodex/commands/SelectCommand.java | 3 ++- src/main/java/decodex/modules/BaseModule.java | 3 ++- .../decodex/modules/base64/Base64Decoder.java | 17 +++++++++++------ 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 17b8b3b5cd..e07c42cdf8 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -5,6 +5,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.parser.Parser; @@ -67,7 +68,7 @@ public void run() { try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); - } catch (ParserException | CommandException | UnknownModuleException | DataManagerException err) { + } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { printErrorMessage(err.getMessage()); } } while (!(command instanceof ExitCommand)); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index aa3483faf4..f305b8f132 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -16,5 +17,5 @@ public Command() { } public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) - throws CommandException, UnknownModuleException, DataManagerException; + throws CommandException, UnknownModuleException, DataManagerException, ModuleException; } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index fb3c1c1c18..97af3b2779 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -4,6 +4,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.BaseModule; import decodex.modules.ModuleManager; @@ -22,7 +23,7 @@ public SelectCommand(String moduleName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) - throws UnknownModuleException, CommandException, DataManagerException { + throws UnknownModuleException, CommandException, DataManagerException, ModuleException { if (moduleName.isBlank()) { throw new CommandException("[-] Missing module name"); } diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/BaseModule.java index 6292f45c33..c3a8552861 100644 --- a/src/main/java/decodex/modules/BaseModule.java +++ b/src/main/java/decodex/modules/BaseModule.java @@ -1,6 +1,7 @@ package decodex.modules; import decodex.data.Data; +import decodex.data.exception.ModuleException; /** * BaseModule serves as a foundation for other modules to be built upon. @@ -28,5 +29,5 @@ public String getDescription() { * * @return Data object containing new processed data. */ - public abstract Data run(Data data); + public abstract Data run(Data data) throws ModuleException; } diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 115115e3a3..19a00bf04d 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -1,6 +1,7 @@ package decodex.modules.base64; import decodex.data.Data; +import decodex.data.exception.ModuleException; import decodex.modules.BaseModule; import java.util.Base64; @@ -27,11 +28,15 @@ public Base64Decoder() { * @return The Data object of the Base64-decoded data. */ @Override - public Data run(Data data) { - Data decodedData; - byte[] originalBytes = data.getRawBytes(); - byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); - decodedData = new Data(base64DecodedBytes); - return decodedData; + public Data run(Data data) throws ModuleException { + try { + Data decodedData; + byte[] originalBytes = data.getRawBytes(); + byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); + decodedData = new Data(base64DecodedBytes); + return decodedData; + } catch (IllegalArgumentException err){ + throw new ModuleException("TESTING MESSAGE FOR BROKEN BASE64 STRING"); + } } } From 404419393f3fdc5c12f13711e415702ad241abbf Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:17:08 +0800 Subject: [PATCH 118/607] Changed the error message for the previous commit. --- src/main/java/decodex/modules/base64/Base64Decoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 19a00bf04d..c1797e13c2 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -36,7 +36,7 @@ public Data run(Data data) throws ModuleException { decodedData = new Data(base64DecodedBytes); return decodedData; } catch (IllegalArgumentException err){ - throw new ModuleException("TESTING MESSAGE FOR BROKEN BASE64 STRING"); + throw new ModuleException(ModuleException.BASE64_DECODING_FAILED_MESSAGE); } } } From 796852956ec3036117b9754879e0f162c0b70c01 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:19:13 +0800 Subject: [PATCH 119/607] Fixed the missing spacing for Base64Decoder --- src/main/java/decodex/modules/base64/Base64Decoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index c1797e13c2..6c23eebd1f 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -35,7 +35,7 @@ public Data run(Data data) throws ModuleException { byte[] base64DecodedBytes = base64Decoder.decode(originalBytes); decodedData = new Data(base64DecodedBytes); return decodedData; - } catch (IllegalArgumentException err){ + } catch (IllegalArgumentException err) { throw new ModuleException(ModuleException.BASE64_DECODING_FAILED_MESSAGE); } } From 9ca205a232dab23c9a8846b463123a1c145af308 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:21:25 +0800 Subject: [PATCH 120/607] Line wrapped the catch exception on Decodex.java --- src/main/java/decodex/Decodex.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index e07c42cdf8..4f43ac2319 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -68,7 +68,8 @@ public void run() { try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); - } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { + } catch (ParserException | CommandException | UnknownModuleException + | DataManagerException | ModuleException err) { printErrorMessage(err.getMessage()); } } while (!(command instanceof ExitCommand)); From 0fd09a19c0303cce822d12356a37fd576d98a798 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:26:01 +0800 Subject: [PATCH 121/607] Changed method name is getUserArgument instead. --- src/main/java/decodex/parser/Parser.java | 6 +++--- src/test/java/decodex/parser/ParserTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 1f7fa874f4..f1772c8def 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -60,7 +60,7 @@ public String getCommandType(String userInput) throws ParserException { * @param userInput The input specified by the user. * @return The argument portion of the user input as an array. */ - public String getUserArguments(String userInput) throws ParserException { + public String getUserArgument(String userInput) throws ParserException { String[] tokens = userInput.split(" ", -1); if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { @@ -126,7 +126,7 @@ private Command craftExitCommand() { * @throws ParserException ParserException */ private Command craftDataCommand(String userInput) throws ParserException { - String arguments = getUserArguments(userInput); + String arguments = getUserArgument(userInput); return new DataCommand(arguments); } @@ -156,7 +156,7 @@ private Command craftResetCommand() { * @throws ParserException ParserException */ private Command craftSelectCommand(String userInput) throws ParserException { - String moduleName = getUserArguments(userInput); + String moduleName = getUserArgument(userInput); return new SelectCommand(moduleName); } diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 3b6b01df32..de04e73d2e 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -42,7 +42,7 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() t String testInput = "test data"; String expectedOutput = "data"; - String arguments = parser.getUserArguments(testInput); + String arguments = parser.getUserArgument(testInput); assertEquals(expectedOutput, arguments); } From 0783a188bf1ac29919300689d59092a0992b015c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:26:15 +0800 Subject: [PATCH 122/607] Fixed some issues with JUnit test. --- src/test/java/decodex/modules/base64/Base64DecoderTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/decodex/modules/base64/Base64DecoderTest.java b/src/test/java/decodex/modules/base64/Base64DecoderTest.java index 68954fecf3..1ca042c6cb 100644 --- a/src/test/java/decodex/modules/base64/Base64DecoderTest.java +++ b/src/test/java/decodex/modules/base64/Base64DecoderTest.java @@ -1,6 +1,7 @@ package decodex.modules.base64; import decodex.data.Data; +import decodex.data.exception.ModuleException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -8,7 +9,7 @@ class Base64DecoderTest { @Test - void run_encodedString_base64DecodedData() { + void run_encodedString_base64DecodedData() throws ModuleException { Base64Decoder base64Decoder = new Base64Decoder(); String encodedString = "dGVzdA=="; String expectedString = "test"; From baefd8c3cd8e416a7924c47661eab174c61059a2 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:29:25 +0800 Subject: [PATCH 123/607] Added ModuleException to throws list for the respective JUnit java files. --- src/test/java/decodex/commands/DataCommandTest.java | 4 +++- src/test/java/decodex/commands/ResetCommandTest.java | 7 +++++-- src/test/java/decodex/commands/SelectCommandTest.java | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 9ce9af1213..45f4a39766 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; @@ -28,7 +29,8 @@ public void run_missingArguments_expectException() { } @Test - public void run_insertData_insertedData() throws CommandException, DataManagerException, UnknownModuleException { + public void run_insertData_insertedData() + throws CommandException, DataManagerException, UnknownModuleException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index c13ee9fb5b..6debe8cda5 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; @@ -17,7 +18,8 @@ class ResetCommandTest { @Test - public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException, UnknownModuleException { + public void run_noChanges_sameOriginalData() + throws CommandException, DataManagerException, UnknownModuleException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); @@ -31,7 +33,8 @@ public void run_noChanges_sameOriginalData() throws CommandException, DataManage } @Test - public void run_newData_sameOriginalData() throws CommandException, DataManagerException, UnknownModuleException { + public void run_newData_sameOriginalData() + throws CommandException, DataManagerException, UnknownModuleException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index c8e62a4d24..da7624a4e0 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -7,6 +7,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -15,7 +16,8 @@ public class SelectCommandTest { @Test - public void run_validModuleName_success() throws UnknownModuleException, CommandException, DataManagerException { + public void run_validModuleName_success() + throws UnknownModuleException, CommandException, DataManagerException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); From 389a96c535e32a6e002252139ebfc9c1bbe2ace6 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 14:39:30 +0800 Subject: [PATCH 124/607] Changed to use lstrip over strip and change to variable name. --- src/main/java/decodex/parser/Parser.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index f1772c8def..7cbcb186da 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -39,12 +39,12 @@ public class Parser { * @return The type of command. */ public String getCommandType(String userInput) throws ParserException { - userInput = userInput.strip(); - if (userInput.isEmpty()) { + String strippedUserInput = userInput.stripLeading(); + if (strippedUserInput.isEmpty()) { throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); } - String[] tokens = userInput.split(" "); + String[] tokens = strippedUserInput.split(" "); boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH_FOR_COMMAND; @@ -61,7 +61,8 @@ public String getCommandType(String userInput) throws ParserException { * @return The argument portion of the user input as an array. */ public String getUserArgument(String userInput) throws ParserException { - String[] tokens = userInput.split(" ", -1); + String strippedUserInput = userInput.stripLeading(); + String[] tokens = strippedUserInput.split(" ", -1); if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); From 5ec94e6eee10653eeea1edfd94cec7925703be33 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 15:27:59 +0800 Subject: [PATCH 125/607] Add checks and JUnit tests for hexencode/hexdecode --- src/main/java/decodex/Decodex.java | 3 +- src/main/java/decodex/commands/Command.java | 3 +- .../java/decodex/commands/SelectCommand.java | 3 +- src/main/java/decodex/modules/BaseModule.java | 3 +- .../java/decodex/modules/hex/HexDecoder.java | 41 ++++++++++++++++++- .../java/decodex/modules/hex/HexEncoder.java | 3 +- .../decodex/commands/DataCommandTest.java | 7 +++- .../decodex/commands/ResetCommandTest.java | 7 +++- .../decodex/commands/SelectCommandTest.java | 5 ++- .../decodex/modules/hex/HexDecoderTest.java | 33 ++++++++++++++- 10 files changed, 96 insertions(+), 12 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 4e1e853175..ffcf18df3e 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -5,6 +5,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.parser.Parser; @@ -61,7 +62,7 @@ public static void main(String[] args) { try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); - } catch (ParserException | CommandException | UnknownModuleException | DataManagerException err) { + } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { printErrorMessage(err.getMessage()); } } while (!(command instanceof ExitCommand)); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index aa3483faf4..f305b8f132 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -16,5 +17,5 @@ public Command() { } public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) - throws CommandException, UnknownModuleException, DataManagerException; + throws CommandException, UnknownModuleException, DataManagerException, ModuleException; } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index fb3c1c1c18..97af3b2779 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -4,6 +4,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.BaseModule; import decodex.modules.ModuleManager; @@ -22,7 +23,7 @@ public SelectCommand(String moduleName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) - throws UnknownModuleException, CommandException, DataManagerException { + throws UnknownModuleException, CommandException, DataManagerException, ModuleException { if (moduleName.isBlank()) { throw new CommandException("[-] Missing module name"); } diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/BaseModule.java index 6292f45c33..c3a8552861 100644 --- a/src/main/java/decodex/modules/BaseModule.java +++ b/src/main/java/decodex/modules/BaseModule.java @@ -1,6 +1,7 @@ package decodex.modules; import decodex.data.Data; +import decodex.data.exception.ModuleException; /** * BaseModule serves as a foundation for other modules to be built upon. @@ -28,5 +29,5 @@ public String getDescription() { * * @return Data object containing new processed data. */ - public abstract Data run(Data data); + public abstract Data run(Data data) throws ModuleException; } diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index dedadff364..817e1eb0c8 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -1,8 +1,11 @@ package decodex.modules.hex; import decodex.data.Data; +import decodex.data.exception.ModuleException; import decodex.modules.BaseModule; import java.util.Arrays; +import java.util.Locale; +import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -27,8 +30,12 @@ public HexDecoder() { * @return new Data object representing the decoded input. */ @Override - public Data run(Data data) { - String inputString = data.toString().replace(" ", ""); + public Data run(Data data) throws ModuleException { + String inputString = data.toString().toLowerCase(Locale.ROOT); + + if (!isValidHex(inputString)) { + throw new ModuleException("Invalid hexadecimal String"); + } String decodedString = Arrays .stream(inputString.split(REGEX_SPLIT_EVERY_2_CHARS)) @@ -37,4 +44,34 @@ public Data run(Data data) { return new Data(decodedString); } + + /** + * Checks if the provided hexadecimal string is valid. + * + * @param hexString Hexadecimal string to be checked. + * @return Boolean if hexadecimal string is valid. + */ + private boolean isValidHex(String hexString) { + boolean isEven = hexString.length() % 2 == 0; + boolean isHexCharacters = isHexCharacters(hexString); + + return isEven && isHexCharacters; + } + + /** + * Checks if the provided hexadecimal string is of valid hexadecimal characters. + * + * @param hexString Hexadecimal string to be checked. + * @return Boolean is hexadecimal string consists of valid characters. + */ + private boolean isHexCharacters(String hexString) { + // Check if String is valid hexadecimal String. + for (int i = 1; i < hexString.length(); i++) { + if (Character.digit(hexString.charAt(i), 16) == -1) { + return false; + } + } + + return true; + } } diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index 5c69ad18f9..202c1ac202 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -13,7 +13,6 @@ public class HexEncoder extends BaseModule { public HexEncoder() { super(MODULE_NAME, MODULE_DESCRIPTION); - } /** @@ -33,4 +32,6 @@ public Data run(Data data) { String encodedString = encodedStringBuilder.toString().trim(); return new Data(encodedString); } + + } diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 9ce9af1213..0407055f88 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; @@ -28,7 +29,8 @@ public void run_missingArguments_expectException() { } @Test - public void run_insertData_insertedData() throws CommandException, DataManagerException, UnknownModuleException { + public void run_insertData_insertedData() + throws CommandException, DataManagerException, UnknownModuleException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); @@ -39,3 +41,6 @@ public void run_insertData_insertedData() throws CommandException, DataManagerEx assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } } + + + diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index c13ee9fb5b..84529148a1 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; @@ -17,7 +18,8 @@ class ResetCommandTest { @Test - public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException, UnknownModuleException { + public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException, + UnknownModuleException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); @@ -31,7 +33,8 @@ public void run_noChanges_sameOriginalData() throws CommandException, DataManage } @Test - public void run_newData_sameOriginalData() throws CommandException, DataManagerException, UnknownModuleException { + public void run_newData_sameOriginalData() + throws CommandException, DataManagerException, UnknownModuleException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index c8e62a4d24..abf98b4315 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -7,15 +7,18 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.ui.Ui; + import org.junit.jupiter.api.Test; public class SelectCommandTest { @Test - public void run_validModuleName_success() throws UnknownModuleException, CommandException, DataManagerException { + public void run_validModuleName_success() + throws UnknownModuleException, CommandException, DataManagerException, ModuleException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/modules/hex/HexDecoderTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java index edb4b6d54e..ac8406a4cf 100644 --- a/src/test/java/decodex/modules/hex/HexDecoderTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -1,14 +1,18 @@ package decodex.modules.hex; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import decodex.data.Data; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; + import org.junit.jupiter.api.Test; class HexDecoderTest { @Test - void run_HexEncoding_expectAllAscii() { + void run_validHex_expectAllAscii() throws ModuleException { String inputString = "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40" + "4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768" + "696a6b6c6d6e6f707172737475767778797a7b7c7d7e"; @@ -21,4 +25,31 @@ void run_HexEncoding_expectAllAscii() { assertEquals(expectedString, result.toString()); } + + @Test + void run_invalidHexChar_expectException() { + String inputString = "AZ"; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); + } + + @Test + void run_invalidHexLength_expectException() { + String inputString = "ZZZ"; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); + } + + @Test + void run_emptyHexInput_expectException() { + String inputString = " "; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); + } } From 25416cf7f0376ad0c364df42541525e1e25b27d1 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 15:28:44 +0800 Subject: [PATCH 126/607] Added more JUnit test for Base64Decoder --- .../modules/base64/Base64DecoderTest.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/test/java/decodex/modules/base64/Base64DecoderTest.java b/src/test/java/decodex/modules/base64/Base64DecoderTest.java index 1ca042c6cb..e6852745fe 100644 --- a/src/test/java/decodex/modules/base64/Base64DecoderTest.java +++ b/src/test/java/decodex/modules/base64/Base64DecoderTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class Base64DecoderTest { @@ -18,4 +19,44 @@ void run_encodedString_base64DecodedData() throws ModuleException { Data decodedData = base64Decoder.run(encodedData); assertEquals(expectedString, decodedData.toString()); } + + @Test + void run_emptyString_emptyDecodedData() throws ModuleException { + Base64Decoder base64Decoder = new Base64Decoder(); + String encodedString = ""; + String expectedString = ""; + + Data encodedData = new Data(encodedString); + Data decodedData = base64Decoder.run(encodedData); + assertEquals(expectedString, decodedData.toString()); + } + + @Test + void run_nullByteBase64EncodedInput_expectNullByte() throws ModuleException { + Base64Decoder base64Decoder = new Base64Decoder(); + String encodedString = "AA=="; + String expectedString = "\00"; + + Data encodedData = new Data(encodedString); + Data decodedData = base64Decoder.run(encodedData); + assertEquals(expectedString, decodedData.toString()); + } + + @Test + void run_stringWithOneSpace_throwsModuleException() { + Base64Decoder base64Decoder = new Base64Decoder(); + String encodedString = " "; + + Data encodedData = new Data(encodedString); + assertThrows(ModuleException.class, () -> base64Decoder.run(encodedData)); + } + + @Test + void run_nullByteString_throwsModuleException() { + Base64Decoder base64Decoder = new Base64Decoder(); + String encodedString = "\00"; + + Data encodedData = new Data(encodedString); + assertThrows(ModuleException.class, () -> base64Decoder.run(encodedData)); + } } From 5a76b5e0a10e048ddbf0d9987dd4625878383eb7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 15:40:59 +0800 Subject: [PATCH 127/607] Add assertions --- build.gradle | 1 + src/main/java/decodex/data/Data.java | 4 ++++ src/main/java/decodex/data/DataManager.java | 3 +++ 3 files changed, 8 insertions(+) diff --git a/build.gradle b/build.gradle index 41a11f95c3..ef702a4af8 100644 --- a/build.gradle +++ b/build.gradle @@ -43,4 +43,5 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true } diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 8a178aec6c..155b2d78fd 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -1,5 +1,7 @@ package decodex.data; +import java.util.Arrays; + /** * The Data class manages the data that is to be encoded or decoded. */ @@ -9,10 +11,12 @@ public class Data { public Data(byte[] rawBytes) { this.rawBytes = rawBytes; + assert !Arrays.equals(this.rawBytes, new byte[0]); } public Data(String data) { rawBytes = data.getBytes(); + assert !Arrays.equals(this.rawBytes, new byte[0]); } public byte[] getRawBytes() { diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index fa571bcf75..5ec0426f6f 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -1,5 +1,7 @@ package decodex.data; +import java.util.Arrays; + import decodex.data.exception.DataManagerException; /** @@ -49,5 +51,6 @@ public void setCurrentData(Data currentData) throws DataManagerException { */ public void resetToOriginalData() { currentData = originalData; + assert Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes()); } } From 161449bf743acaf1454824c8371f9ca89f0d6b3b Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 15:42:55 +0800 Subject: [PATCH 128/607] Remove extra lines --- src/main/java/decodex/modules/hex/HexEncoder.java | 1 - src/test/java/decodex/commands/DataCommandTest.java | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index 202c1ac202..234a62f7da 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -33,5 +33,4 @@ public Data run(Data data) { return new Data(encodedString); } - } diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 0407055f88..45f4a39766 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -41,6 +41,3 @@ public void run_insertData_insertedData() assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } } - - - From df035d0e0f294e06d7b408ebed5bc7904b478673 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 15:49:48 +0800 Subject: [PATCH 129/607] Added the public logger variable for global use in Decodex application. --- src/main/java/decodex/Decodex.java | 3 +++ src/main/java/decodex/modules/base64/Base64Encoder.java | 1 + 2 files changed, 4 insertions(+) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 4f43ac2319..b661b4609e 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -13,9 +13,12 @@ import decodex.ui.Ui; import java.util.Scanner; +import java.util.logging.Logger; public class Decodex { + public static Logger logger = Logger.getLogger(Decodex.class.getName()); + /** * Necessary objects to be initialized for Decodex to work properly. */ diff --git a/src/main/java/decodex/modules/base64/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java index aa0b5641d0..ec3a26aa62 100644 --- a/src/main/java/decodex/modules/base64/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -5,6 +5,7 @@ import java.util.Base64; + /** * Base64Encoder serves to carry out the Base64 encoding operations. */ From 9a6a39b2b5356aaf2025d26d683c65451f2d7c62 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 15:52:33 +0800 Subject: [PATCH 130/607] Removed the weird additional line --- src/main/java/decodex/modules/base64/Base64Encoder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/decodex/modules/base64/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java index ec3a26aa62..aa0b5641d0 100644 --- a/src/main/java/decodex/modules/base64/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -5,7 +5,6 @@ import java.util.Base64; - /** * Base64Encoder serves to carry out the Base64 encoding operations. */ From e14706152b39dad9552882b29058d6b70bff5f58 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:02:04 +0800 Subject: [PATCH 131/607] Add assertion of number of modules in `ListCommand` --- src/main/java/decodex/commands/ListCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 6f1e4b360a..56ddf7fa40 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -16,6 +16,7 @@ public ListCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { BaseModule[] modules = moduleManager.getModules(); + assert modules.length > 0 : "number of modules should be greater than 0"; StringBuilder moduleListString = new StringBuilder(); int maxNameWidth = 0; From 1f98e2cee115d169777f02d7b633163db7e21b78 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 16:02:28 +0800 Subject: [PATCH 132/607] Resolve suggested changes --- src/main/java/decodex/modules/hex/HexDecoder.java | 2 +- src/main/java/decodex/modules/hex/HexEncoder.java | 2 +- .../java/decodex/modules/hex/HexDecoderTest.java | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 817e1eb0c8..392c42157c 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -31,7 +31,7 @@ public HexDecoder() { */ @Override public Data run(Data data) throws ModuleException { - String inputString = data.toString().toLowerCase(Locale.ROOT); + String inputString = data.toString().toLowerCase(); if (!isValidHex(inputString)) { throw new ModuleException("Invalid hexadecimal String"); diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index 234a62f7da..06a0f5f5e3 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -29,7 +29,7 @@ public Data run(Data data) { encodedStringBuilder.append(String.format("%02x", b)); } - String encodedString = encodedStringBuilder.toString().trim(); + String encodedString = encodedStringBuilder.toString(); return new Data(encodedString); } diff --git a/src/test/java/decodex/modules/hex/HexDecoderTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java index ac8406a4cf..f40cc4a330 100644 --- a/src/test/java/decodex/modules/hex/HexDecoderTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -52,4 +52,17 @@ void run_emptyHexInput_expectException() { assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); } + + @Test + void run_nullHexInput_expectException() throws ModuleException { + String inputString = "00"; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + Data result = hexDecoder.run(inputData); + + String expectedString = "\00"; + + assertEquals(expectedString, result.toString()); + } } From af2cabe62341859e12d66d03f6da057ff5f1c59f Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 16:05:14 +0800 Subject: [PATCH 133/607] Add messages to assert --- src/main/java/decodex/data/Data.java | 4 ++-- src/main/java/decodex/data/DataManager.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 155b2d78fd..e774487b9b 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -11,12 +11,12 @@ public class Data { public Data(byte[] rawBytes) { this.rawBytes = rawBytes; - assert !Arrays.equals(this.rawBytes, new byte[0]); + assert !Arrays.equals(this.rawBytes, new byte[0]) : "Data is non-empty"; } public Data(String data) { rawBytes = data.getBytes(); - assert !Arrays.equals(this.rawBytes, new byte[0]); + assert !Arrays.equals(this.rawBytes, new byte[0]) : "Data is non-empty"; } public byte[] getRawBytes() { diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index 5ec0426f6f..eee8087be4 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -51,6 +51,7 @@ public void setCurrentData(Data currentData) throws DataManagerException { */ public void resetToOriginalData() { currentData = originalData; - assert Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes()); + assert Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes()) : "Current data is same as " + + "original data"; } } From 8da8cec3c7a107a033eef10f2a209362604abcba Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:17:16 +0800 Subject: [PATCH 134/607] Log all modules loaded into `ModuleManager` --- src/main/java/decodex/modules/ModuleManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 8b81725bb1..25847a12bd 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -1,22 +1,31 @@ package decodex.modules; +import decodex.Decodex; import decodex.data.exception.UnknownModuleException; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; +import java.util.logging.Level; +import java.util.logging.Logger; /** * ModuleManager maintains the list of available modules and returns selected modules. */ public class ModuleManager { + private Logger logger = Decodex.logger; + /** * Array of available modules. */ private final BaseModule[] modules = {new Base64Encoder(), new Base64Decoder(), new HexEncoder(), new HexDecoder()}; public ModuleManager() { + // Log available modules + for (BaseModule module : modules) { + logger.log(Level.FINE, String.format("Loaded %s", module.getName())); + } } public BaseModule[] getModules() { From 67c08d917c55920cf280acca6a433483cd8108cb Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 16:20:47 +0800 Subject: [PATCH 135/607] Add logging for commands --- src/main/java/decodex/Decodex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index b661b4609e..71a0a09888 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -67,7 +67,7 @@ public void run() { do { printPromptHeader(); String userInput = in.nextLine(); - + logger.fine(userInput); try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); From 3bf36e83cf767c8816bf7f08a94f026b1f76c80f Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 15:27:59 +0800 Subject: [PATCH 136/607] Add checks and JUnit tests for hexencode/hexdecode --- src/main/java/decodex/Decodex.java | 4 ++ .../java/decodex/modules/hex/HexDecoder.java | 41 ++++++++++++++++++- .../java/decodex/modules/hex/HexEncoder.java | 3 +- .../decodex/commands/DataCommandTest.java | 3 ++ .../decodex/commands/ResetCommandTest.java | 5 +++ .../decodex/commands/SelectCommandTest.java | 1 + .../decodex/modules/hex/HexDecoderTest.java | 33 ++++++++++++++- 7 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index b661b4609e..80225c8aeb 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -71,8 +71,12 @@ public void run() { try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); +<<<<<<< refs/remotes/AY2122S1-CS2113T-T10-3/master } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { +======= + } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { +>>>>>>> Add checks and JUnit tests for hexencode/hexdecode printErrorMessage(err.getMessage()); } } while (!(command instanceof ExitCommand)); diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index dedadff364..817e1eb0c8 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -1,8 +1,11 @@ package decodex.modules.hex; import decodex.data.Data; +import decodex.data.exception.ModuleException; import decodex.modules.BaseModule; import java.util.Arrays; +import java.util.Locale; +import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -27,8 +30,12 @@ public HexDecoder() { * @return new Data object representing the decoded input. */ @Override - public Data run(Data data) { - String inputString = data.toString().replace(" ", ""); + public Data run(Data data) throws ModuleException { + String inputString = data.toString().toLowerCase(Locale.ROOT); + + if (!isValidHex(inputString)) { + throw new ModuleException("Invalid hexadecimal String"); + } String decodedString = Arrays .stream(inputString.split(REGEX_SPLIT_EVERY_2_CHARS)) @@ -37,4 +44,34 @@ public Data run(Data data) { return new Data(decodedString); } + + /** + * Checks if the provided hexadecimal string is valid. + * + * @param hexString Hexadecimal string to be checked. + * @return Boolean if hexadecimal string is valid. + */ + private boolean isValidHex(String hexString) { + boolean isEven = hexString.length() % 2 == 0; + boolean isHexCharacters = isHexCharacters(hexString); + + return isEven && isHexCharacters; + } + + /** + * Checks if the provided hexadecimal string is of valid hexadecimal characters. + * + * @param hexString Hexadecimal string to be checked. + * @return Boolean is hexadecimal string consists of valid characters. + */ + private boolean isHexCharacters(String hexString) { + // Check if String is valid hexadecimal String. + for (int i = 1; i < hexString.length(); i++) { + if (Character.digit(hexString.charAt(i), 16) == -1) { + return false; + } + } + + return true; + } } diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index 5c69ad18f9..202c1ac202 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -13,7 +13,6 @@ public class HexEncoder extends BaseModule { public HexEncoder() { super(MODULE_NAME, MODULE_DESCRIPTION); - } /** @@ -33,4 +32,6 @@ public Data run(Data data) { String encodedString = encodedStringBuilder.toString().trim(); return new Data(encodedString); } + + } diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 45f4a39766..0407055f88 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -41,3 +41,6 @@ public void run_insertData_insertedData() assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } } + + + diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index 6debe8cda5..7a974441c7 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -18,8 +18,13 @@ class ResetCommandTest { @Test +<<<<<<< refs/remotes/AY2122S1-CS2113T-T10-3/master public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException, UnknownModuleException, ModuleException { +======= + public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException, + UnknownModuleException, ModuleException { +>>>>>>> Add checks and JUnit tests for hexencode/hexdecode DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index da7624a4e0..abf98b4315 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -11,6 +11,7 @@ import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.ui.Ui; + import org.junit.jupiter.api.Test; public class SelectCommandTest { diff --git a/src/test/java/decodex/modules/hex/HexDecoderTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java index edb4b6d54e..ac8406a4cf 100644 --- a/src/test/java/decodex/modules/hex/HexDecoderTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -1,14 +1,18 @@ package decodex.modules.hex; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import decodex.data.Data; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; + import org.junit.jupiter.api.Test; class HexDecoderTest { @Test - void run_HexEncoding_expectAllAscii() { + void run_validHex_expectAllAscii() throws ModuleException { String inputString = "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40" + "4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768" + "696a6b6c6d6e6f707172737475767778797a7b7c7d7e"; @@ -21,4 +25,31 @@ void run_HexEncoding_expectAllAscii() { assertEquals(expectedString, result.toString()); } + + @Test + void run_invalidHexChar_expectException() { + String inputString = "AZ"; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); + } + + @Test + void run_invalidHexLength_expectException() { + String inputString = "ZZZ"; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); + } + + @Test + void run_emptyHexInput_expectException() { + String inputString = " "; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); + } } From 0916ddaeb7f90130fafc5eb8e4d038969282586b Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 15:42:55 +0800 Subject: [PATCH 137/607] Remove extra lines --- src/main/java/decodex/modules/hex/HexEncoder.java | 1 - src/test/java/decodex/commands/DataCommandTest.java | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index 202c1ac202..234a62f7da 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -33,5 +33,4 @@ public Data run(Data data) { return new Data(encodedString); } - } diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/DataCommandTest.java index 0407055f88..45f4a39766 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/DataCommandTest.java @@ -41,6 +41,3 @@ public void run_insertData_insertedData() assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } } - - - From 971b5f4dfc3fe91544bebfbb04fd34156d908cf6 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 16:02:28 +0800 Subject: [PATCH 138/607] Resolve suggested changes --- src/main/java/decodex/modules/hex/HexDecoder.java | 2 +- src/main/java/decodex/modules/hex/HexEncoder.java | 2 +- .../java/decodex/modules/hex/HexDecoderTest.java | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 817e1eb0c8..392c42157c 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -31,7 +31,7 @@ public HexDecoder() { */ @Override public Data run(Data data) throws ModuleException { - String inputString = data.toString().toLowerCase(Locale.ROOT); + String inputString = data.toString().toLowerCase(); if (!isValidHex(inputString)) { throw new ModuleException("Invalid hexadecimal String"); diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index 234a62f7da..06a0f5f5e3 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -29,7 +29,7 @@ public Data run(Data data) { encodedStringBuilder.append(String.format("%02x", b)); } - String encodedString = encodedStringBuilder.toString().trim(); + String encodedString = encodedStringBuilder.toString(); return new Data(encodedString); } diff --git a/src/test/java/decodex/modules/hex/HexDecoderTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java index ac8406a4cf..f40cc4a330 100644 --- a/src/test/java/decodex/modules/hex/HexDecoderTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -52,4 +52,17 @@ void run_emptyHexInput_expectException() { assertThrows(ModuleException.class, () -> hexDecoder.run(inputData)); } + + @Test + void run_nullHexInput_expectException() throws ModuleException { + String inputString = "00"; + HexDecoder hexDecoder = new HexDecoder(); + Data inputData = new Data(inputString); + + Data result = hexDecoder.run(inputData); + + String expectedString = "\00"; + + assertEquals(expectedString, result.toString()); + } } From c5c18553e8d7084491ba47597498fbe16d31e5ad Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:22:51 +0800 Subject: [PATCH 139/607] Replace `logger.log` to `logger.fine` --- src/main/java/decodex/modules/ModuleManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 25847a12bd..b4854a19a1 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -24,7 +24,7 @@ public class ModuleManager { public ModuleManager() { // Log available modules for (BaseModule module : modules) { - logger.log(Level.FINE, String.format("Loaded %s", module.getName())); + logger.fine(String.format("Loaded %s", module.getName())); } } From 3e2b42f63cb2839189e905cb4a9754e85fa90c5b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 16:23:58 +0800 Subject: [PATCH 140/607] Added logging for Decodex's exception catching. --- src/main/java/decodex/Decodex.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index b661b4609e..f59a849e29 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -13,12 +13,14 @@ import decodex.ui.Ui; import java.util.Scanner; +import java.util.logging.Level; import java.util.logging.Logger; public class Decodex { public static Logger logger = Logger.getLogger(Decodex.class.getName()); + /** * Necessary objects to be initialized for Decodex to work properly. */ @@ -47,6 +49,7 @@ public Decodex() { * Initializes the necessary Objects for Decodex. */ public static void initDecodex() { + logger.setLevel(Level.INFO); dataManager = new DataManager(); moduleManager = new ModuleManager(); parser = new Parser(); @@ -74,6 +77,7 @@ public void run() { } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { printErrorMessage(err.getMessage()); + logger.fine(err.getMessage()); } } while (!(command instanceof ExitCommand)); From b9d6fa656f1ba2c6b4f2d5367fc5aec498342d7b Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 16:28:01 +0800 Subject: [PATCH 141/607] Add indication of user input --- src/main/java/decodex/Decodex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 71a0a09888..ae44f82f25 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -67,7 +67,7 @@ public void run() { do { printPromptHeader(); String userInput = in.nextLine(); - logger.fine(userInput); + logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); From 59e6990c8dcb8b25e86a1a6698d58eb3e9a5165e Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 16:28:17 +0800 Subject: [PATCH 142/607] Log all modules selected to run --- src/main/java/decodex/modules/ModuleManager.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 8b81725bb1..8faf2f786a 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -1,16 +1,22 @@ package decodex.modules; +import decodex.Decodex; import decodex.data.exception.UnknownModuleException; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * ModuleManager maintains the list of available modules and returns selected modules. */ public class ModuleManager { + private Logger logger = Decodex.logger; + /** * Array of available modules. */ @@ -33,6 +39,7 @@ public BaseModule[] getModules() { public BaseModule selectModule(String moduleName) throws UnknownModuleException { for (BaseModule module : modules) { if (module.getName().equals(moduleName)) { + logger.fine(String.format("Selected %s", module.getName())); return module; } } From 2643b5a7c475f0be4bbaedb45b4119a18a32381c Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 16:32:18 +0800 Subject: [PATCH 143/607] Add requested change --- src/main/java/decodex/data/Data.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index e774487b9b..3605616034 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -11,12 +11,12 @@ public class Data { public Data(byte[] rawBytes) { this.rawBytes = rawBytes; - assert !Arrays.equals(this.rawBytes, new byte[0]) : "Data is non-empty"; + assert this.rawBytes.length > 0 : "Data is non-empty"; } public Data(String data) { rawBytes = data.getBytes(); - assert !Arrays.equals(this.rawBytes, new byte[0]) : "Data is non-empty"; + assert this.rawBytes.length > 0 : "Data is non-empty"; } public byte[] getRawBytes() { From 5450b83adb0de2caf00fe8c88f2cde05496ddf2c Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 16:38:15 +0800 Subject: [PATCH 144/607] Resolve suggested changes - Remove unused imports --- src/main/java/decodex/modules/hex/HexDecoder.java | 2 -- src/test/java/decodex/modules/hex/HexDecoderTest.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 392c42157c..0dd596b8e1 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -4,8 +4,6 @@ import decodex.data.exception.ModuleException; import decodex.modules.BaseModule; import java.util.Arrays; -import java.util.Locale; -import java.util.regex.Pattern; import java.util.stream.Collectors; /** diff --git a/src/test/java/decodex/modules/hex/HexDecoderTest.java b/src/test/java/decodex/modules/hex/HexDecoderTest.java index f40cc4a330..08c46f7af2 100644 --- a/src/test/java/decodex/modules/hex/HexDecoderTest.java +++ b/src/test/java/decodex/modules/hex/HexDecoderTest.java @@ -4,9 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import decodex.data.Data; -import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; - import org.junit.jupiter.api.Test; class HexDecoderTest { From 84681c0ab354b8195cf52fe4e12b32e14b252f06 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 16:38:26 +0800 Subject: [PATCH 145/607] Remove erroneous assertion --- src/main/java/decodex/data/Data.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 3605616034..8b0d2184d3 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -11,12 +11,10 @@ public class Data { public Data(byte[] rawBytes) { this.rawBytes = rawBytes; - assert this.rawBytes.length > 0 : "Data is non-empty"; } public Data(String data) { rawBytes = data.getBytes(); - assert this.rawBytes.length > 0 : "Data is non-empty"; } public byte[] getRawBytes() { From d0512c4e93f152b4c55ad75f752d41bd52e92f2e Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 16:46:44 +0800 Subject: [PATCH 146/607] Fix merge conflict --- src/main/java/decodex/Decodex.java | 4 ---- src/main/java/decodex/modules/hex/HexDecoder.java | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 80225c8aeb..b661b4609e 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -71,12 +71,8 @@ public void run() { try { command = parser.parseCommand(userInput); command.run(dataManager, moduleManager, ui); -<<<<<<< refs/remotes/AY2122S1-CS2113T-T10-3/master } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { -======= - } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { ->>>>>>> Add checks and JUnit tests for hexencode/hexdecode printErrorMessage(err.getMessage()); } } while (!(command instanceof ExitCommand)); diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 392c42157c..0dd596b8e1 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -4,8 +4,6 @@ import decodex.data.exception.ModuleException; import decodex.modules.BaseModule; import java.util.Arrays; -import java.util.Locale; -import java.util.regex.Pattern; import java.util.stream.Collectors; /** From 2ee47d071e3df72afc20dc2bf2c8feb9d0216f07 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 16:47:55 +0800 Subject: [PATCH 147/607] Added asserts to the command type string. --- src/main/java/decodex/parser/Parser.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 7cbcb186da..2b11fb54b4 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -51,7 +51,10 @@ public String getCommandType(String userInput) throws ParserException { if (isInvalidTokensLength) { throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); } - return tokens[COMMAND_INDEX]; + + String commandType = tokens[COMMAND_INDEX]; + assert !commandType.isEmpty() : "Command Type should not be empty"; + return commandType; } /** From 757cfc763c2310ed8b676bf65415a6e6c36be5f4 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 16:52:47 +0800 Subject: [PATCH 148/607] Resolve suggested changes --- src/main/java/decodex/commands/ListCommand.java | 2 +- src/main/java/decodex/modules/hex/HexDecoder.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 56ddf7fa40..72e8e1cba3 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -16,7 +16,7 @@ public ListCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { BaseModule[] modules = moduleManager.getModules(); - assert modules.length > 0 : "number of modules should be greater than 0"; + assert modules.length > 0 : "Number of modules should be greater than 0"; StringBuilder moduleListString = new StringBuilder(); int maxNameWidth = 0; diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 0dd596b8e1..988a2ed746 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -32,7 +32,7 @@ public Data run(Data data) throws ModuleException { String inputString = data.toString().toLowerCase(); if (!isValidHex(inputString)) { - throw new ModuleException("Invalid hexadecimal String"); + throw new ModuleException("Invalid hexadecimal string"); } String decodedString = Arrays From abed594fb03146e2bb59bb555f16a5cf8bfd749d Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 17:20:11 +0800 Subject: [PATCH 149/607] Add assertions - Add command assertion - Remove redundant exit --- src/main/java/decodex/Decodex.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index d7969b0aae..616f049b9f 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -73,6 +73,7 @@ public void run() { logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); + assert command != null : "Command supposed to not be null."; command.run(dataManager, moduleManager, ui); } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { @@ -80,8 +81,6 @@ public void run() { logger.fine(err.getMessage()); } } while (!(command instanceof ExitCommand)); - - System.exit(0); } /** From b7793e026aa0ebe3d023cd8d6e10c2ceba204e17 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 11 Oct 2021 17:22:44 +0800 Subject: [PATCH 150/607] Add requested change --- src/main/java/decodex/Decodex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 616f049b9f..8d07bf61b0 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -73,7 +73,7 @@ public void run() { logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); - assert command != null : "Command supposed to not be null."; + assert command != null : "Command should not be null"; command.run(dataManager, moduleManager, ui); } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { From 41a2ba2aa860058ffec9e4b2fff143372f86219a Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 17:38:14 +0800 Subject: [PATCH 151/607] Removed Logo and changed greeting message. --- src/main/java/decodex/Decodex.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index d7969b0aae..d53302c477 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -30,16 +30,6 @@ public class Decodex { private static Scanner in; private static Ui ui; - - /** - * Logo to be changed - possibly to a welcome message instead for Decodex. - */ - private static final String LOGO = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - public Decodex() { printGreeting(); initDecodex(); @@ -88,7 +78,9 @@ public void run() { * Prints the greeting message. */ public static void printGreeting() { - System.out.println("Hello from\n" + LOGO); + System.out.println("======================== Decodex ===========================\n" + + "Welcome to Decodex, transforming data with ease\n" + + "============================================================"); } /** From 66b9a6d014be9ad5c28243cfc8d1efa6fd598781 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 17:41:28 +0800 Subject: [PATCH 152/607] Updated Expected.txt --- text-ui-test/EXPECTED.TXT | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 92330fa3b4..328cc06121 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,10 +1,6 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - +======================== Decodex =========================== +Welcome to Decodex, transforming data with ease +============================================================ Decodex > [-] Unknown command, please enter a valid command Decodex > [-] Unknown command, please enter a valid command Decodex > [+] Input: I am a lab rat From d673009d6c59796c7b7fea415b7071a86cbf66c1 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 11 Oct 2021 19:22:03 +0800 Subject: [PATCH 153/607] Base UG to work on. --- docs/UserGuide.md | 55 ++++++++++++++++++------------------- docs/images/carbon(10).png | Bin 0 -> 12312 bytes docs/images/carbon(6).png | Bin 0 -> 43671 bytes docs/images/carbon(8).png | Bin 0 -> 29019 bytes docs/images/carbon(9).png | Bin 0 -> 15251 bytes 5 files changed, 27 insertions(+), 28 deletions(-) create mode 100644 docs/images/carbon(10).png create mode 100644 docs/images/carbon(6).png create mode 100644 docs/images/carbon(8).png create mode 100644 docs/images/carbon(9).png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..9b8cab7f4e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,42 +1,41 @@ -# User Guide +# Decodex User Guide -## Introduction +# Introduction -{Give a product intro} +Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly transform data from one format to another with extreme ease**. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. -## Quick Start +This user guide is tailored for CTF players who have basic understanding of information security concepts. -{Give steps to get started quickly} +> ❗ This user guide is tailored for CTF players who have basic understanding of information security concepts. -1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +![carbon(6).png](images/carbon(6).png) -## Features +# Table of Contents +1. Introduction +2. Table of Contents +3. Terminologies +4. Features +5. Command Summary +--- -{Give detailed description of each feature} +# Terminologies -### Adding a todo: `todo` -Adds a new item to the list of todo items. +This section serves to help the user better understand the terminologies used in this user guide. -Format: `todo n/TODO_NAME d/DEADLINE` +| Terminology | Definition | +|-------------|------------| +| | | +| | | -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. -Example of usage: +# Features + -`todo n/Write the rest of the User Guide d/next week` -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` +# Command Summary -## FAQ - -**Q**: How do I transfer my data to another computer? - -**A**: {your answer here} - -## Command Summary - -{Give a 'cheat sheet' of commands here} - -* Add todo `todo n/TODO_NAME d/DEADLINE` +| Action | Command Format | Example Usage | +|--------|----------------|---------------| +| | | | +| | | | +| | | | \ No newline at end of file diff --git a/docs/images/carbon(10).png b/docs/images/carbon(10).png new file mode 100644 index 0000000000000000000000000000000000000000..e29f9d07dcabddfd463bd57bedaa694726094321 GIT binary patch literal 12312 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 sEo1>U+gMMbya;tv7V!8Z+pz>% literal 0 HcmV?d00001 diff --git a/docs/images/carbon(6).png b/docs/images/carbon(6).png new file mode 100644 index 0000000000000000000000000000000000000000..4486798170fd1d4e4bb45f45050f128ee323afd6 GIT binary patch literal 43671 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 literal 0 HcmV?d00001 diff --git a/docs/images/carbon(8).png b/docs/images/carbon(8).png new file mode 100644 index 0000000000000000000000000000000000000000..2734968065bbaedbc2de7c992e652307f19f629b GIT binary patch literal 29019 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%0DPt2rT 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

fH0MPx>BFkzD;T<(LUz`zJ>O zADY#j*|a1}e%V0efTFNv`Ntau0e)uYpxHG%HY^kHI~)f;fPTb-AQ0|&403Y}CZ7h` zE^vwb$+EaQ=Kjn)4vTL0qGICH=wN1O260JqX^+`#A+y9wz+MA%eEQKt<6}WHWiEVE zYzK7R>fw^GX00XP=)fb7YrrFpZ~d}t^n+;h!Hw4qZTdE*&wSAeG`9gb2wh7JtbKgc z{@?^TfcaBYG&Nx3fl8r9z zQ=&GpeEjcgGom|4-;skPa233#i6pSIvS{rNe6vMM{2lm<)_RG@M?&K}o*BN8?CpC(8ac5w4A0ye|@`BoOBUgl-;yzExfY&vt zm><-;uYvJw+$@_%YwfFLNscK^y$5K!Y!xu-%)wC>nU-@V`~eQsJJs%Lsgx-pg8v)z C?nd$e literal 0 HcmV?d00001 From 90770558b34928a7f75c26278961847a236f8dae Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 20:36:01 +0800 Subject: [PATCH 154/607] Rename `BaseModule` to `Module` --- .../java/decodex/commands/ListCommand.java | 8 +-- .../java/decodex/commands/SelectCommand.java | 4 +- .../modules/{BaseModule.java => Module.java} | 66 +++++++++---------- .../java/decodex/modules/ModuleManager.java | 14 ++-- .../decodex/modules/base64/Base64Decoder.java | 4 +- .../decodex/modules/base64/Base64Encoder.java | 4 +- .../java/decodex/modules/hex/HexDecoder.java | 4 +- .../java/decodex/modules/hex/HexEncoder.java | 4 +- .../decodex/modules/ModuleManagerTest.java | 2 +- 9 files changed, 53 insertions(+), 57 deletions(-) rename src/main/java/decodex/modules/{BaseModule.java => Module.java} (76%) diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 72e8e1cba3..cb50f2ba1d 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -1,7 +1,7 @@ package decodex.commands; import decodex.data.DataManager; -import decodex.modules.BaseModule; +import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -15,14 +15,14 @@ public ListCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { - BaseModule[] modules = moduleManager.getModules(); + Module[] modules = moduleManager.getModules(); assert modules.length > 0 : "Number of modules should be greater than 0"; StringBuilder moduleListString = new StringBuilder(); int maxNameWidth = 0; // Find number of characters of the longest module name - for (BaseModule module : modules) { + for (Module module : modules) { String moduleName = module.getName(); if (moduleName.length() > maxNameWidth) { maxNameWidth = moduleName.length(); @@ -30,7 +30,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { } // Prepare and format list of modules - for (BaseModule module : modules) { + for (Module module : modules) { String moduleName = module.getName(); String moduleDescription = module.getDescription(); moduleListString.append(String.format(" %-" + maxNameWidth + "s - %s\n", moduleName, moduleDescription)); diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index 97af3b2779..1d1406ce00 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -6,7 +6,7 @@ import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; -import decodex.modules.BaseModule; +import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.ui.Ui; @@ -28,7 +28,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throw new CommandException("[-] Missing module name"); } - BaseModule module = moduleManager.selectModule(moduleName); + Module module = moduleManager.selectModule(moduleName); Data newData = module.run(dataManager.getCurrentData()); dataManager.setCurrentData(newData); diff --git a/src/main/java/decodex/modules/BaseModule.java b/src/main/java/decodex/modules/Module.java similarity index 76% rename from src/main/java/decodex/modules/BaseModule.java rename to src/main/java/decodex/modules/Module.java index c3a8552861..b18d8008de 100644 --- a/src/main/java/decodex/modules/BaseModule.java +++ b/src/main/java/decodex/modules/Module.java @@ -1,33 +1,33 @@ -package decodex.modules; - -import decodex.data.Data; -import decodex.data.exception.ModuleException; - -/** - * BaseModule serves as a foundation for other modules to be built upon. - */ -public abstract class BaseModule { - - protected String name; - protected String description; - - public BaseModule(String name, String description) { - this.name = name; - this.description = description; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - /** - * Performs data processing on the provided Data object. Abstract method to be implemented by other modules. - * - * @return Data object containing new processed data. - */ - public abstract Data run(Data data) throws ModuleException; -} +package decodex.modules; + +import decodex.data.Data; +import decodex.data.exception.ModuleException; + +/** + * Module serves as a foundation for other modules to be built upon. + */ +public abstract class Module { + + protected String name; + protected String description; + + public Module(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + /** + * Performs data processing on the provided Data object. Abstract method to be implemented by other modules. + * + * @return Data object containing new processed data. + */ + public abstract Data run(Data data) throws ModuleException; +} diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 301f42dc0b..10b24c2b45 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -6,10 +6,6 @@ import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; -import java.util.logging.Level; -import java.util.logging.Logger; - -import java.util.logging.Level; import java.util.logging.Logger; /** @@ -22,16 +18,16 @@ public class ModuleManager { /** * Array of available modules. */ - private final BaseModule[] modules = {new Base64Encoder(), new Base64Decoder(), new HexEncoder(), new HexDecoder()}; + private final Module[] modules = {new Base64Encoder(), new Base64Decoder(), new HexEncoder(), new HexDecoder()}; public ModuleManager() { // Log available modules - for (BaseModule module : modules) { + for (Module module : modules) { logger.fine(String.format("Loaded %s", module.getName())); } } - public BaseModule[] getModules() { + public Module[] getModules() { return modules; } @@ -42,8 +38,8 @@ public BaseModule[] getModules() { * @return Selected module. * @throws UnknownModuleException Not a module name of an available module. */ - public BaseModule selectModule(String moduleName) throws UnknownModuleException { - for (BaseModule module : modules) { + public Module selectModule(String moduleName) throws UnknownModuleException { + for (Module module : modules) { if (module.getName().equals(moduleName)) { logger.fine(String.format("Selected %s", module.getName())); return module; diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 6c23eebd1f..7daba92311 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -2,14 +2,14 @@ import decodex.data.Data; import decodex.data.exception.ModuleException; -import decodex.modules.BaseModule; +import decodex.modules.Module; import java.util.Base64; /** * Base64Encoder serves to carry out the Base64 decoding operations. */ -public class Base64Decoder extends BaseModule { +public class Base64Decoder extends Module { private final Base64.Decoder base64Decoder; diff --git a/src/main/java/decodex/modules/base64/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java index aa0b5641d0..eea1250c6e 100644 --- a/src/main/java/decodex/modules/base64/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -1,14 +1,14 @@ package decodex.modules.base64; import decodex.data.Data; -import decodex.modules.BaseModule; +import decodex.modules.Module; import java.util.Base64; /** * Base64Encoder serves to carry out the Base64 encoding operations. */ -public class Base64Encoder extends BaseModule { +public class Base64Encoder extends Module { private final Base64.Encoder base64Encoder; diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 988a2ed746..3a14339511 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -2,14 +2,14 @@ import decodex.data.Data; import decodex.data.exception.ModuleException; -import decodex.modules.BaseModule; +import decodex.modules.Module; import java.util.Arrays; import java.util.stream.Collectors; /** * The HexDecoder class converts a hexadecimal string back into its raw value. */ -public class HexDecoder extends BaseModule { +public class HexDecoder extends Module { public static final String MODULE_NAME = "hexdecode"; public static final String MODULE_DESCRIPTION = "Converts a hexadecimal string back into its raw value"; diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index 06a0f5f5e3..fddd4e9ca4 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -1,12 +1,12 @@ package decodex.modules.hex; import decodex.data.Data; -import decodex.modules.BaseModule; +import decodex.modules.Module; /** * The HexEncoder class converts the input string to hexadecimal bytes. */ -public class HexEncoder extends BaseModule { +public class HexEncoder extends Module { public static final String MODULE_NAME = "hexencode"; public static final String MODULE_DESCRIPTION = "Converts the input string to hexadecimal bytes"; diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index c50e1e7920..30b6aa48b1 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -15,7 +15,7 @@ public void selectModule_base64encode_success() throws UnknownModuleException { String moduleName = "base64encode"; ModuleManager moduleManager = new ModuleManager(); - BaseModule module = moduleManager.selectModule(moduleName); + Module module = moduleManager.selectModule(moduleName); assertTrue(module instanceof Base64Encoder); } From 94baa0e7cd936f5772688c2bb8dc8df05fb1c20d Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 23:22:34 +0800 Subject: [PATCH 155/607] Add assertion to ensure data is not null in`Data` constructor --- src/main/java/decodex/data/Data.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 8b0d2184d3..50cbfe05e9 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -1,7 +1,5 @@ package decodex.data; -import java.util.Arrays; - /** * The Data class manages the data that is to be encoded or decoded. */ @@ -14,6 +12,7 @@ public Data(byte[] rawBytes) { } public Data(String data) { + assert data != null; rawBytes = data.getBytes(); } From 28227848e9b6a0be32f5cbe401bc42259c2eb6bb Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 23:32:01 +0800 Subject: [PATCH 156/607] Update assertion message --- src/main/java/decodex/data/Data.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 50cbfe05e9..2b24c246b9 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -12,7 +12,7 @@ public Data(byte[] rawBytes) { } public Data(String data) { - assert data != null; + assert data != null : "Data string should not be null"; rawBytes = data.getBytes(); } From d240ab802bc753e8163f813d62c184da8a6c9a73 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 23:49:17 +0800 Subject: [PATCH 157/607] Update user guide with `list` and `select` command --- docs/UserGuide.md | 55 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 9b8cab7f4e..d7f78e8d13 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,4 +1,4 @@ -# Decodex User Guide +# Decodex User Guide # Introduction @@ -10,32 +10,55 @@ This user guide is tailored for CTF players who have basic understanding of info ![carbon(6).png](images/carbon(6).png) -# Table of Contents -1. Introduction -2. Table of Contents -3. Terminologies -4. Features -5. Command Summary ---- +# Table of Contents +- [Introduction](#introduction) +- [Terminologies](#terminologies) +- [Features](#features) + - [List available modules: `list`](#list-available-modules-list) + - [Selection of module: `select`](#selection-of-module-select) +- [Command Summary](#command-summary) # Terminologies This section serves to help the user better understand the terminologies used in this user guide. | Terminology | Definition | -|-------------|------------| +| ----------- | ---------- | +| | | | | | -| | | # Features - +## List available modules: `list` + +Shows a list of all available modules. + +Format: `list` + +![carbon(8).png](images/carbon(8).png) + +## Selection of module: `select` + +Selects a module and processes the data accordingly. Subsequent selection of modules will process the transformed data output from the previous module. + +Format: `select ` + +`moduleName` is the name of an available module supported by the program. + +Examples: + +- `select base64decode` Decode the base64 encoded data. +- `select hexencode` Encode the data into hexadecimal format. + +![carbon(9).png](images/carbon(9).png) # Command Summary -| Action | Command Format | Example Usage | -|--------|----------------|---------------| -| | | | -| | | | -| | | | \ No newline at end of file +| Action | Command Format | Example Usage | +| ------------------------ | -------------------- | --------------------------------------------- | +| | | | +| List available modules | `list` | `list` | +| Select processing module | `select `select hexencode` | +| | | | +| | | | \ No newline at end of file From 354fa49047be01419665da938817a8e02ee85057 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 23:51:44 +0800 Subject: [PATCH 158/607] Change DataCommand to InputCommand --- .../commands/{DataCommand.java => InputCommand.java} | 6 +++--- src/main/java/decodex/parser/Parser.java | 6 +++--- .../{DataCommandTest.java => InputCommandTest.java} | 6 +++--- src/test/java/decodex/parser/ParserTest.java | 4 ++-- text-ui-test/input.txt | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) rename src/main/java/decodex/commands/{DataCommand.java => InputCommand.java} (79%) rename src/test/java/decodex/commands/{DataCommandTest.java => InputCommandTest.java} (87%) diff --git a/src/main/java/decodex/commands/DataCommand.java b/src/main/java/decodex/commands/InputCommand.java similarity index 79% rename from src/main/java/decodex/commands/DataCommand.java rename to src/main/java/decodex/commands/InputCommand.java index 64bc27d686..e583adf68d 100644 --- a/src/main/java/decodex/commands/DataCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -6,13 +6,13 @@ import decodex.modules.ModuleManager; import decodex.ui.Ui; -public class DataCommand extends Command { +public class InputCommand extends Command { - public static final String COMMAND_WORD = "data"; + public static final String COMMAND_WORD = "input"; private final String dataString; - public DataCommand(String dataString) { + public InputCommand(String dataString) { super(); this.dataString = dataString; } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 2b11fb54b4..68248a04cf 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -1,7 +1,7 @@ package decodex.parser; import decodex.commands.Command; -import decodex.commands.DataCommand; +import decodex.commands.InputCommand; import decodex.commands.ExitCommand; import decodex.commands.ListCommand; import decodex.commands.ResetCommand; @@ -95,7 +95,7 @@ public Command parseCommand(String userInput) throws ParserException { case ExitCommand.COMMAND_WORD: command = craftExitCommand(); break; - case DataCommand.COMMAND_WORD: + case InputCommand.COMMAND_WORD: command = craftDataCommand(userInput); break; case ListCommand.COMMAND_WORD: @@ -131,7 +131,7 @@ private Command craftExitCommand() { */ private Command craftDataCommand(String userInput) throws ParserException { String arguments = getUserArgument(userInput); - return new DataCommand(arguments); + return new InputCommand(arguments); } /** diff --git a/src/test/java/decodex/commands/DataCommandTest.java b/src/test/java/decodex/commands/InputCommandTest.java similarity index 87% rename from src/test/java/decodex/commands/DataCommandTest.java rename to src/test/java/decodex/commands/InputCommandTest.java index 45f4a39766..0f6e9cffbf 100644 --- a/src/test/java/decodex/commands/DataCommandTest.java +++ b/src/test/java/decodex/commands/InputCommandTest.java @@ -16,7 +16,7 @@ import decodex.modules.ModuleManager; import decodex.ui.Ui; -public class DataCommandTest { +public class InputCommandTest { @Test public void run_missingArguments_expectException() { @@ -24,7 +24,7 @@ public void run_missingArguments_expectException() { ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); String dataString = ""; - Command testCommand = new DataCommand(dataString); + Command testCommand = new InputCommand(dataString); assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui)); } @@ -35,7 +35,7 @@ public void run_insertData_insertedData() ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); String dataString = "something"; - Command testCommand = new DataCommand(dataString); + Command testCommand = new InputCommand(dataString); testCommand.run(dataManager, moduleManager, ui); Data testData = new Data(dataString); assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index de04e73d2e..1dbde576eb 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -1,7 +1,7 @@ package decodex.parser; import decodex.commands.Command; -import decodex.commands.DataCommand; +import decodex.commands.InputCommand; import decodex.commands.ExitCommand; import decodex.commands.ListCommand; import decodex.commands.ResetCommand; @@ -55,7 +55,7 @@ void parseCommand_userInputSpecifyingValidData_expectDataCommand() throws Parser Command command = parser.parseCommand(userInput); - assertTrue(command instanceof DataCommand); + assertTrue(command instanceof InputCommand); } diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index eec04f7583..b8f535569f 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,6 +1,6 @@ whoami bring me thanos -data I am a lab rat +input I am a lab rat select base64encode select base64decode select hexencode From ef1afb35cff407bc36d77c09037815ffaf210bea Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 23:54:50 +0800 Subject: [PATCH 159/607] Minor edit in command details --- docs/UserGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d7f78e8d13..934d7f640d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -58,7 +58,7 @@ Examples: | Action | Command Format | Example Usage | | ------------------------ | -------------------- | --------------------------------------------- | | | | | -| List available modules | `list` | `list` | -| Select processing module | `select `select hexencode` | +| | | | +| | | | | | | | | | | | \ No newline at end of file From f445a44e533a06e692d224053e88840ff5553d9f Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 11 Oct 2021 23:55:40 +0800 Subject: [PATCH 160/607] Fix format --- docs/UserGuide.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 934d7f640d..30ff4b9223 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -55,10 +55,8 @@ Examples: # Command Summary -| Action | Command Format | Example Usage | -| ------------------------ | -------------------- | --------------------------------------------- | -| | | | -| | | | -| | | | -| | | | -| | | | \ No newline at end of file +| Action | Command Format | Example Usage | +| ------ | -------------- | ------------- | +| | | | +| | | | +| | | | \ No newline at end of file From 0e20758c2583e62306fd3f1a9226bb092682ae9c Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 11 Oct 2021 23:56:49 +0800 Subject: [PATCH 161/607] Update parser and tests with InputCommand --- src/main/java/decodex/parser/Parser.java | 4 ++-- src/test/java/decodex/parser/ParserTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 68248a04cf..2df9be8b41 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -96,7 +96,7 @@ public Command parseCommand(String userInput) throws ParserException { command = craftExitCommand(); break; case InputCommand.COMMAND_WORD: - command = craftDataCommand(userInput); + command = craftInputCommand(userInput); break; case ListCommand.COMMAND_WORD: command = craftListCommand(); @@ -129,7 +129,7 @@ private Command craftExitCommand() { * @return The Data command * @throws ParserException ParserException */ - private Command craftDataCommand(String userInput) throws ParserException { + private Command craftInputCommand(String userInput) throws ParserException { String arguments = getUserArgument(userInput); return new InputCommand(arguments); } diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 1dbde576eb..3f25f81774 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -50,8 +50,8 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() t /* The JUnit test methods below are for testing "normal" usage flows for the respective commands.*/ @Test - void parseCommand_userInputSpecifyingValidData_expectDataCommand() throws ParserException { - String userInput = "data dummyData"; + void parseCommand_userInputSpecifyingValidData_expectInputCommand() throws ParserException { + String userInput = "input dummyData"; Command command = parser.parseCommand(userInput); From cf4c3164bff2ec91e7bb0a8ce0f5a93a71490cbe Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 07:04:49 +0800 Subject: [PATCH 162/607] Changes to some of the header ([x]) of the exception messages. --- src/main/java/decodex/data/exception/ModuleException.java | 2 +- src/main/java/decodex/parser/Parser.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/data/exception/ModuleException.java b/src/main/java/decodex/data/exception/ModuleException.java index b391ac5e85..1d8653bc47 100644 --- a/src/main/java/decodex/data/exception/ModuleException.java +++ b/src/main/java/decodex/data/exception/ModuleException.java @@ -6,7 +6,7 @@ public class ModuleException extends Exception { public static final String BASE64_DECODING_FAILED_MESSAGE = "[x] " - + "This data does not seem to be in Base64 format"; + + "Your data does not seem to be in Base64 format"; public ModuleException(String message) { super(message); diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 2b11fb54b4..d6dab68ecd 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -77,7 +77,7 @@ public String getUserArgument(String userInput) throws ParserException { return joinedArguments; } String singleArgument = argumentTokens[0]; - return singleArgument; + return singleArgument.stripLeading(); } /** @@ -108,7 +108,7 @@ public Command parseCommand(String userInput) throws ParserException { command = craftSelectCommand(userInput); break; default: - throw new ParserException("[-] Unknown command, please enter a valid command"); + throw new ParserException("[x] Unknown command, please enter a valid command"); } return command; } From 2efbc199604f716dcd9dc362f37542b5b46e07a9 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 07:10:56 +0800 Subject: [PATCH 163/607] Reverted some changes. --- src/main/java/decodex/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index d6dab68ecd..024ac51976 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -77,7 +77,7 @@ public String getUserArgument(String userInput) throws ParserException { return joinedArguments; } String singleArgument = argumentTokens[0]; - return singleArgument.stripLeading(); + return singleArgument; } /** From 87f1ec7e8716bf646f88cd571d2af5a4025d267c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 11:46:32 +0800 Subject: [PATCH 164/607] Modified the text-ui-test expected.txt --- text-ui-test/EXPECTED.TXT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 328cc06121..2e52110b4d 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,8 +1,8 @@ ======================== Decodex =========================== Welcome to Decodex, transforming data with ease ============================================================ -Decodex > [-] Unknown command, please enter a valid command -Decodex > [-] Unknown command, please enter a valid command +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Unknown command, please enter a valid command Decodex > [+] Input: I am a lab rat Decodex > [+] Output: SSBhbSBhIGxhYiByYXQ= Decodex > [+] Output: I am a lab rat From 76e6fdd4b3e96653971483b668747fc99863f1fd Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 11:49:27 +0800 Subject: [PATCH 165/607] Made another change to text-ui-test that i missed out. --- text-ui-test/EXPECTED.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 2e52110b4d..65d29a408c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -8,5 +8,5 @@ Decodex > [+] Output: SSBhbSBhIGxhYiByYXQ= Decodex > [+] Output: I am a lab rat Decodex > [+] Output: 4920616d2061206c616220726174 Decodex > [+] Reverted all changes -Decodex > [-] Unknown command, please enter a valid command +Decodex > [x] Unknown command, please enter a valid command Decodex > Goodbye! \ No newline at end of file From 30e93a204233acda06f10d800f9a2716ecda2f9d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 12:59:33 +0800 Subject: [PATCH 166/607] Updated methods for getting module name and getting input data for "input" command. --- src/main/java/decodex/parser/Parser.java | 32 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 024ac51976..03bc650978 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -19,6 +19,7 @@ public class Parser { * Specifies the index of the tokens where specific parameters can be found. */ private static final int COMMAND_INDEX = 0; + private static final int MODULE_NAME_INDEX_IN_TOKENS = 0; /** @@ -80,6 +81,31 @@ public String getUserArgument(String userInput) throws ParserException { return singleArgument; } + public String getModuleName(String userInput) throws ParserException { + String strippedUserInput = userInput.stripLeading(); + String[] tokens = strippedUserInput.split(" ", -1); + + String[] argumentTokens = Arrays.stream(Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length)) + .filter(value -> !value.isBlank()) + .toArray(size -> new String[size]); + if (argumentTokens.length == 0) { + throw new ParserException("[-] Module name is missing"); + } + return argumentTokens[MODULE_NAME_INDEX_IN_TOKENS]; + } + + public String getInputString(String userInput) throws ParserException { + String strippedUserInput = userInput.stripLeading(); + String[] tokens = strippedUserInput.split(" ", -1); + + if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { + throw new ParserException("[-] Your input is empty, please do \"input i am a string\""); + } + String argumentString = String.join(" ", + Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length)); + return argumentString; + } + /** * Parses the user input provided by user and returns its respective command. Returns a null object if the command * is invalid. @@ -130,8 +156,8 @@ private Command craftExitCommand() { * @throws ParserException ParserException */ private Command craftDataCommand(String userInput) throws ParserException { - String arguments = getUserArgument(userInput); - return new DataCommand(arguments); + String inputData = getInputString(userInput); + return new DataCommand(inputData); } /** @@ -160,7 +186,7 @@ private Command craftResetCommand() { * @throws ParserException ParserException */ private Command craftSelectCommand(String userInput) throws ParserException { - String moduleName = getUserArgument(userInput); + String moduleName = getModuleName(userInput); return new SelectCommand(moduleName); } From 6dc4952784da3cbf7c67d083be5205fadcb1e7dc Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 13:12:35 +0800 Subject: [PATCH 167/607] Removed unnecessary method and constants --- src/main/java/decodex/parser/Parser.java | 30 +++--------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 03bc650978..99cda1cdd0 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -31,7 +31,6 @@ public class Parser { * Specifies the valid length of the tokens and used to check validity of tokens. */ private static final int VALID_TOKENS_LENGTH_FOR_COMMAND = 1; - private static final int VALID_TOKENS_LENGTH_FOR_ARGUMENTS = 2; /** * Returns the type of command that user has entered. @@ -58,29 +57,6 @@ public String getCommandType(String userInput) throws ParserException { return commandType; } - /** - * Returns the Argument portion of the user input as a whole string. - * - * @param userInput The input specified by the user. - * @return The argument portion of the user input as an array. - */ - public String getUserArgument(String userInput) throws ParserException { - String strippedUserInput = userInput.stripLeading(); - String[] tokens = strippedUserInput.split(" ", -1); - - if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { - throw new ParserException(ParserException.MISSING_COMMAND_ARGS_MESSAGE); - } - String[] argumentTokens = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); - - if (argumentTokens.length > 1) { - String joinedArguments = String.join(" ", argumentTokens); - return joinedArguments; - } - String singleArgument = argumentTokens[0]; - return singleArgument; - } - public String getModuleName(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); String[] tokens = strippedUserInput.split(" ", -1); @@ -98,11 +74,11 @@ public String getInputString(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); String[] tokens = strippedUserInput.split(" ", -1); - if (tokens.length < VALID_TOKENS_LENGTH_FOR_ARGUMENTS) { - throw new ParserException("[-] Your input is empty, please do \"input i am a string\""); - } String argumentString = String.join(" ", Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length)); + if (argumentString.isEmpty()) { + throw new ParserException("[-] Your input is empty, please do \"input i am a string\""); + } return argumentString; } From 3a760f5a4b088e16e598e9c937ed3e7776042ea7 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 13:26:33 +0800 Subject: [PATCH 168/607] Fixed the issue with the github actions --- src/test/java/decodex/parser/ParserTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index de04e73d2e..cf5f226a58 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -42,7 +42,7 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() t String testInput = "test data"; String expectedOutput = "data"; - String arguments = parser.getUserArgument(testInput); + String arguments = parser.getInputString(testInput); assertEquals(expectedOutput, arguments); } From d1c3b048a5df779c3e4e0f4546713a23635c4258 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 12 Oct 2021 16:45:16 +0800 Subject: [PATCH 169/607] Add "" to output of data --- src/main/java/decodex/commands/InputCommand.java | 2 +- src/main/java/decodex/commands/SelectCommand.java | 2 +- text-ui-test/EXPECTED.TXT | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index e583adf68d..ca05198795 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -24,6 +24,6 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) thr } Data userData = new Data(dataString); dataManager.setOriginalData(userData); - System.out.printf("[+] Input: %s\n", dataString); + System.out.printf("[+] Input: \"%s\"\n", dataString); } } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index 1d1406ce00..f0676b1d4e 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -32,6 +32,6 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) Data newData = module.run(dataManager.getCurrentData()); dataManager.setCurrentData(newData); - System.out.printf("[+] Output: %s\n", newData.toString()); + System.out.printf("[+] Output: \"%s\"\n", newData.toString()); } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 328cc06121..b0edca2e3c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -3,10 +3,10 @@ Welcome to Decodex, transforming data with ease ============================================================ Decodex > [-] Unknown command, please enter a valid command Decodex > [-] Unknown command, please enter a valid command -Decodex > [+] Input: I am a lab rat -Decodex > [+] Output: SSBhbSBhIGxhYiByYXQ= -Decodex > [+] Output: I am a lab rat -Decodex > [+] Output: 4920616d2061206c616220726174 +Decodex > [+] Input: "I am a lab rat" +Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "4920616d2061206c616220726174" Decodex > [+] Reverted all changes Decodex > [-] Unknown command, please enter a valid command Decodex > Goodbye! \ No newline at end of file From 77598860c5a64f7a227fb5ec173bffef52f11ab5 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 12 Oct 2021 17:45:34 +0800 Subject: [PATCH 170/607] Update user guide --- docs/UserGuide.md | 21 +++++++++++++++++++++ docs/images/carbon(1).png | Bin 0 -> 12069 bytes docs/images/carbon(6).png | Bin 43671 -> 47135 bytes docs/images/carbon(9).png | Bin 15251 -> 14736 bytes 4 files changed, 21 insertions(+) create mode 100644 docs/images/carbon(1).png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 30ff4b9223..a7f387dd23 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -14,8 +14,10 @@ This user guide is tailored for CTF players who have basic understanding of info - [Introduction](#introduction) - [Terminologies](#terminologies) - [Features](#features) + - [Input of data: `input`](#input-of-data-input) - [List available modules: `list`](#list-available-modules-list) - [Selection of module: `select`](#selection-of-module-select) + - [Resetting of data: `reset`](#resetting-of-data-reset) - [Command Summary](#command-summary) # Terminologies @@ -30,6 +32,19 @@ This section serves to help the user better understand the terminologies used in # Features +## Input of data: `input` + +Stores the data to be processed by modules. + +Format: `input ` + +Examples: + +- `input HelloWorld!` Enter plain text as data. +- `input SGVsbG9Xb3JsZA==` Enter base64-encoded data. + +![carbon(1).png](images/carbon(1).png) + ## List available modules: `list` Shows a list of all available modules. @@ -53,6 +68,12 @@ Examples: ![carbon(9).png](images/carbon(9).png) +## Resetting of data: `reset` + +Resets the transformed data back to the original input. + +Format: `reset` + # Command Summary | Action | Command Format | Example Usage | diff --git a/docs/images/carbon(1).png b/docs/images/carbon(1).png new file mode 100644 index 0000000000000000000000000000000000000000..fbf20216724b4520c93e171b728203694eb91f3e GIT binary patch literal 12069 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�b|7XM6_@0A_$vNGL>XV}>g-bjK1b7}-L-ag@+1)x zR8vwUrE4e;S^azn!s-u8RjP{$bDLz9<|QDq1GIiwV{9C zGt=pr+|$&2i5U*dmwH9@goh0q?$Q12$RI{;fT?~`-Z8^GqH_!kyx^hoRFmncL9_u0 z86{Z(ngV1tKOkmWCo=`5Ew%vjJEV8QArh0~_h}gc_EQV)0O^qfk)(ECpsCPg5|Dp zV!Do5uv=g~2t@#UIiR$gouxmU{R9c|hc*QnjX~N6yH@vl_I|N2R#|7vhzy3^fr^Ff zz&9pO406%ukX}%k%TXoN3LG56_&dI?@z2)4Y+d?d2{tQ(g=J!yd71JDzFRVo7|}~$ zPm@He@G2c80@C4?IVYB^i|x<$=8wm7e?{N_v%?u5_UAe93fUc2#5v91z}$Xo$ST)D zNS8GE{;zI(<^U(D8YkqnxEZ3>n!x*y+@Im;UfJp0c?KVc=7kiNoQwg>!F_0MO}_hZZUC- zuwnjCe3mX7(&teR)FhH(Scw5cqr!2b4z@pSAIyFFf=c(x!}G;;hgu2OynWYXAx=&g z^erF?ct#d_SoF(T+zG<==4wfTJtrsU-TU`%^Jt`~)kCyu)Nc(nCdJ3!v9ma=pWe0^ z&i$bGw7aHD)wOs#GCA)@=T^SnVhWmKAYt{cU5>~gSIcn7HlyVdkms%u+~%1S7{o!Z zv=RKr3Jbw&Gf$TO5Y~1L7Icj<3X=+6XTO{PLjJ`$QZ0Z5G{*{j1T`zAlMn?4Wm%36 zYZ8X0!VA&3Lb(Wj?8*=KP6V3suEB0wp)b{;KvnOgBddnQ|Kz0Di&%aJ_}9DE<`N;< z_l}dmHvUIZ@(6}aRzyf6n6ROvi4{)>x`tiGX9!>SIYEd-P9=0DPZ>9iRu$D1;D7V{ zG->0{u~r`#UL17~lrVzx&ZgMXGd@m6D5YC`arj&{&HS+DYO$=P=reyrpwGD;-QPy- z0OI`uQ-H8=sYf&YvQBJkYPm1FR2!ideZmbwPc$`o#N9PkuJ3yEuph8$!gGJurNS#? zlFoG(^fGNyFk*Jpb&pc$1Ibd+n9x-j&?!n08NkZSS> zUJG-55wmF7Jp#T8C`bhKb59lK^5a!RHBDZJo9Kp0{E|_Y_pnah>25zz4y_k(PHc5xQ;XmI(0uZ(eEL3~GXE%O zg=h>7X#k97=o(EynmDg_Eko~N^%>@T+D0pg3lz35_^rRCiZ|z5 zw91?r!>v`_n4e%ZcP#!czm%u@Hw>7o=o9#yFrZyY8S26D&QGq;r}a3@A|&F;_KYbucl|7&_jdiMZXOmu6q@2?09wq zq5frZ&a&qV5=ZgCQ2dY_%kleXxP73nOk+Y!Y}4>cbP7kY8uut7eDmqX_U5k?`Ea_D zW#PHH4zF-yt8)lVg;}?M#SE6^YkrZI8=O;XD8TD-e3cn>m8P!l`?KZ@P6$pj70^~#M+-~K*0Qjg3)0QH@LYSo5IT;Oon=XJaq%@2wDBlK^}i4^ zf#(|On=CaVx4LY5sZbz;u~-x}*nRv>RZ%T|llaf}eK>^<{NTgt0F?|D%W7Jcbw69J zCGI*mLdiP2n|TaIRhIMCkV7rD^d838SVIQMi_z4kCOR_!tG|5%`{b*$RxyV-Ha;;i za&^_V^AB<4$JBzweqD5}Y05v};Cd6ly^fA2sIX6;KKZfM7(FEr2u71{Szs`@I65+x z*Gzc;mvze|3V)rjc()7EpUs1$gg>U^1*TY`p1doge8dOAb>>s+>5@D_r5#-rmA{dN zISVYe@1_lg2JYY^!n$)KN}1`MNy!&N=B6WX0F~G@KqW9`Do#s%zXhnvqpF4&mVGy0zx8J$VudiMU9X>Ff(2+N_MI}!LHqwv+R5yt^lmPn-ImLF%et1Z#-H+ zlD~;MRGU^>CB2!GdRaK0yFC+)3w=3+q7pp5A*pzYvTj`^iC<#I11?KXmXXh`7 zEwC^SDwox!bS@g56rzHfalR(0qx?^r*>mzCS6PtB;q^f+x3HQ4?B!nSH{im?DOoz{ zF(yCfFXSGzL2|XZoKC3_vS^C`R0k9INDDO*eIbgA-ws}-H>)CseYrXRsnsp4%Q3VW zD5y1Yd*iYC(89fDsOiYUs79m8*} z*Uy)&=9Y0-^SL1ozXZYkwJ0KTF<)0p+28^=mqCe@UBi)69z&Du-zlP@l2()bwQ|DvADSM?e+C=FX;5u{N z2G~a9b6>SPcZRY*OL%jcu+2Egb(uS-Jl-iS|38Tcy9+}ujV?#m&_5l@1rLOYDYKyh z`P;!(f*_>1LBtU4XespKH#vdxj$k+C5ynusE&CEhM21oX11y@XqUyEb{=B#_F3I7L zxV^qhQ;0WZJ3xyNC^b^a@;kjxc>!3to#+YtyR$|R$knGT z0C4d1E=0S_j@{Eq=uTw=ObitZD-m~9Ox!=pvC5b41=vm2_r~Ov z0++i~5S&W#SKq*uUQ3_>#cCS!q;#qNyfnkQVe}d!j2;%WTHmiKvlB9n=zP@*v=&ST79MfbJxp~QYf>0ZSdAo^ z@XJnI$IMpOY9A&CmHLkA-*i9i-oKxUGoMQr7yP+26|sE_`y|$cfIRVqH(z^^q0&ed zWx$@scWa1JJlwQ;mzn`-`m4ps0G&K_*+Q*8x9O(MEDdo?54m;-eH%1gUfxGlq9@nV zC*dntPCICQWhrIXt3P^DJ7(q=i^l|eZjVPSOsQE@4LM~L-S>L=SM)@2H*5yb&A?9 z+NtaYmV)vPj(0d3Pa0o2WRDL&1j_Ksv!3uJUrSA+cFRXdzPGouurGjAOhtbUp%e&z z?@G)hr}NmDpEoNJP?eOFbnx|3Q&;ykSzhdnDo$^1YI-2+t_i&f(vuksX@h2Mts8F( zBHg{}K1@+^Y{$I_XLz}tE*Ok`lG-!?TzdsXAV+ajQIA@jBP39%;IU&=D@i&aO9oNo z((>vZ?nZ~TZklBH3UDBwZNB6y1s21z@A90>k64r|czCmsEcZDH5d9U3s_$4!;Pv5p zXL?9*^JKkDOa$Tiz{3BtQkPG#A_4!|q69>b$z4zkSW&XlGTiV3g=<5wf;hJQ3%9e zy=#CYU8JCuP>Nvx8Nx$K%=A%2l0rELM7tJav68WaKLI@U;~Dv!P^{^A1{$-%M3Eg; zKl9^phAhC&2bDRPs|!_mmVJ;Nm+(%R;d3u4tXwY?QQr(Cta)j6uG|{u20CBvnt!?|8;uvWs? z%Z!e0m!{6mRnFx8XXMcx$2EX%i`#wKL2~h?fbPuMYm#tnmh2Cv(-Cgk1qAw`SZZlf z9s(AUEmzjuehq+sH*s#mWye&eJJJSXcB0{gyXT{N4+Vc|G9CdKrV3ClxQ_NwbGp&{ zxWPJG)kl|<70Q_Gs*tk6~Z!Rg9)LQq%vNn0pn?;@HwAHjd25CiT& z5r9=(2^IsF$riK*5Ftr5$uHwn?uwR;n z`?qYNiwbe>!+&i9kC2d@EGqe%iS+WTOcHb@HU9spjQ#IXZ~;sQ9RH`)!+D^vc_&lq zPbc&_)Sxa~xfWDbkPcx&_5Y?ax^#S9P}%F>vTY$yupRsE-8<@7KsbTpRXHrD6dYej z2PoLiqLlpeB>ng01hp5A!N(e)Xqyia{^x_2Zbfw-l{as2{HY}+(|jY_Yk61UAWOjM z5QPBDq`XfTf{O}Vx Yo*mtvPO(y_g@nqFo+=hU{3q~#0AROV@&Et; literal 0 HcmV?d00001 diff --git a/docs/images/carbon(6).png b/docs/images/carbon(6).png index 4486798170fd1d4e4bb45f45050f128ee323afd6..4c46db6571cac1b1a40c7694e781d04d36e2b5aa 100644 GIT binary patch literal 47135 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 diff --git a/docs/images/carbon(9).png b/docs/images/carbon(9).png index b3c63755e45c3407d3a57b0cddbb5aaeaf9e7cda..a18668b8bf8e9e6d18ad43a43bf246b9568c1ca9 100644 GIT binary patch literal 14736 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 literal 15251 zcmdseXHZjJ)Gn{06h+hsC{<7cf(WQc2Puj`=)EQiiu58K0!kH-CLjT&BRxcV4+g0= zz<@}NG-*O;ArRW#!T0;_{dH%)nLD>J6VLJFoSnVbUgcTOS|?IROO=U{osouyhDq(d ziXIIOEtH1lA6ok3;K~MB{2dL=MH)2~MFXF=OJl^R2A%0dw*!V(s=0~^G`xy;Sfd8d zUpSy)unxYWdT;At#0N%``epW)ks=mX6KZMNjmpm_S4E<;)Pt`)gfN#62`SOHHX@P) z^#zBDPIDBn(ydun7B##5G=GEk$#hIE*==U7lG~7yR`7fL4Hnn7gDVj%fBbEJ`KeED z=KTJqQ>Ru2iwK?7Zocel3YWX_$L1SBiZN}Fr(mw&@8uV`&Lwa0Q6YGMV`9M4Vua1~ zS04Z0%(e3ew&3h@b_!OAxmwn~`RL*8h|Ri^2`gV;Nh#s1I!)#)8B5Cv+XJPshhNgv zuMWcJ-9sU@F-_an7kt+{*TDZP#w8EG72S~|5hmKY-Y8@+LahR4JdM;3uRm+@U7aCn z9fN?0Chu?znz)=kyk6&dt*j)I5sDpNul>RC_vfAE;M)s<^-IxlP8H*SZ*yHV#qSo5 zdVz)g;5c&lq_f;QTYr~R^x^_qg7)yrsNZkn!7is7j(`5%U`Y!(Jo&&L%y4*uV8Z>q z^L;qt?;s}1qJM8LU^s z8*1=LxMgf??qXbwBpcuNfBNpv_IHb+^>_8($1SdYB^#T#r06vi>zzffUoL-V|A}o@ zZN6B2ap`b*7c&HU5tF!MhVE4p*XuWX1?#cS)w{)yVgj1>A8h;VeOwcXD@~5W_Ff+3 zF}s2(t25`e&`Cbn6i@o>eLxP_eo}WY(AP2BBFHmgcj9aNT7(4KXN91Hr)%0DPt2rT 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

fH0MPx>BFkzD;T<(LUz`zJ>O zADY#j*|a1}e%V0efTFNv`Ntau0e)uYpxHG%HY^kHI~)f;fPTb-AQ0|&403Y}CZ7h` zE^vwb$+EaQ=Kjn)4vTL0qGICH=wN1O260JqX^+`#A+y9wz+MA%eEQKt<6}WHWiEVE zYzK7R>fw^GX00XP=)fb7YrrFpZ~d}t^n+;h!Hw4qZTdE*&wSAeG`9gb2wh7JtbKgc z{@?^TfcaBYG&Nx3fl8r9z zQ=&GpeEjcgGom|4-;skPa233#i6pSIvS{rNe6vMM{2lm<)_RG@M?&K}o*BN8?CpC(8ac5w4A0ye|@`BoOBUgl-;yzExfY&vt zm><-;uYvJw+$@_%YwfFLNscK^y$5K!Y!xu-%)wC>nU-@V`~eQsJJs%Lsgx-pg8v)z C?nd$e From b555d6ec88bffc51e25eacc3a808acc96d7b6aff Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 12 Oct 2021 17:59:22 +0800 Subject: [PATCH 171/607] Update user guide to fix bug --- docs/UserGuide.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index a7f387dd23..1ae429b83b 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -38,6 +38,9 @@ Stores the data to be processed by modules. Format: `input ` +> 💡 `data` will be treated as text. Also, note that the program will take whatever characters after the first space character after the `input` command as `data`. + + Examples: - `input HelloWorld!` Enter plain text as data. From 388f5f2748ac77d477d5e32240c7aceef3f85627 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 12 Oct 2021 18:09:28 +0800 Subject: [PATCH 172/607] Added JavaDocs to 2 of the methods and made the exception message more general. --- src/main/java/decodex/parser/Parser.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 99cda1cdd0..2f7b9e2298 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -57,6 +57,12 @@ public String getCommandType(String userInput) throws ParserException { return commandType; } + /** + * Returns the Module Name that user has specified. + * + * @param userInput The input specified by the user. + * @return The Module name. + */ public String getModuleName(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); String[] tokens = strippedUserInput.split(" ", -1); @@ -70,6 +76,12 @@ public String getModuleName(String userInput) throws ParserException { return argumentTokens[MODULE_NAME_INDEX_IN_TOKENS]; } + /** + * Returns the input data string that user has specified. + * + * @param userInput The input specified by the user. + * @return The input data string. + */ public String getInputString(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); String[] tokens = strippedUserInput.split(" ", -1); @@ -77,7 +89,7 @@ public String getInputString(String userInput) throws ParserException { String argumentString = String.join(" ", Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length)); if (argumentString.isEmpty()) { - throw new ParserException("[-] Your input is empty, please do \"input i am a string\""); + throw new ParserException("[-] Your input is empty"); } return argumentString; } From 59d4f1c0637112b54fdad7c52dc1f97b0c810751 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 12 Oct 2021 18:45:46 +0800 Subject: [PATCH 173/607] Update screenshot to better explain --- docs/UserGuide.md | 7 ++++--- docs/images/carbon(1).png | Bin 12069 -> 18201 bytes docs/images/carbon(5).png | Bin 0 -> 12069 bytes 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 docs/images/carbon(5).png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 1ae429b83b..0df998bc50 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -6,7 +6,7 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CT This user guide is tailored for CTF players who have basic understanding of information security concepts. -> ❗ This user guide is tailored for CTF players who have basic understanding of information security concepts. +> :information_source: This user guide is tailored for CTF players who have basic understanding of information security concepts. ![carbon(6).png](images/carbon(6).png) @@ -38,8 +38,7 @@ Stores the data to be processed by modules. Format: `input ` -> 💡 `data` will be treated as text. Also, note that the program will take whatever characters after the first space character after the `input` command as `data`. - +> :information_source: `data` will be treated as text. Examples: @@ -48,6 +47,8 @@ Examples: ![carbon(1).png](images/carbon(1).png) +> :exclamation: Note that the program will take in whatever characters after the first space character as seen in the second command in the above screenshot. Observe the leading and trailing whitespaces. + ## List available modules: `list` Shows a list of all available modules. diff --git a/docs/images/carbon(1).png b/docs/images/carbon(1).png index fbf20216724b4520c93e171b728203694eb91f3e..a612fc1da04e341a8859e8c193be308dd99e3bba 100644 GIT binary patch literal 18201 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�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 Yo*mtvPO(y_g@nqFo+=hU{3q~#0AROV@&Et; diff --git a/docs/images/carbon(5).png b/docs/images/carbon(5).png new file mode 100644 index 0000000000000000000000000000000000000000..fbf20216724b4520c93e171b728203694eb91f3e GIT binary patch literal 12069 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�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 Yo*mtvPO(y_g@nqFo+=hU{3q~#0AROV@&Et; literal 0 HcmV?d00001 From 67e548556ef387acf5d6404fa7ae27f655ed6b89 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 12 Oct 2021 20:44:17 +0800 Subject: [PATCH 174/607] Update user guide with `exit` command and command summary --- docs/UserGuide.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 30ff4b9223..1521eaf52a 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -53,10 +53,18 @@ Examples: ![carbon(9).png](images/carbon(9).png) +## Exiting the program: `exit` + +Exit the program. + +Format: `exit` + # Command Summary -| Action | Command Format | Example Usage | -| ------ | -------------- | ------------- | -| | | | -| | | | -| | | | \ No newline at end of file +| Action | Command Format | Example Usage | +|--------------------------|-------------------------|-----------------------------------------------| +| Enter input data | `data` `` | `data HelloWorld!`
`data SGVsbG9Xb3JsZA==` | +| List available modules | `list` | `list` | +| Select processing module | `select` `` | `select base64decode`
`select hexencode` | +| Reset to original data | `reset` | `reset` | +| Exit program | `exit` | `select hexencode` | \ No newline at end of file From 8873b6d48c6a587e1ce7680cee23db5d7e1b44f7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 12 Oct 2021 21:47:02 +0800 Subject: [PATCH 175/607] Add requested changes --- docs/UserGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 0df998bc50..bfbbcde3cf 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -42,12 +42,12 @@ Format: `input ` Examples: -- `input HelloWorld!` Enter plain text as data. - `input SGVsbG9Xb3JsZA==` Enter base64-encoded data. +- input  hello world Enter plain text as data. ![carbon(1).png](images/carbon(1).png) -> :exclamation: Note that the program will take in whatever characters after the first space character as seen in the second command in the above screenshot. Observe the leading and trailing whitespaces. +> :exclamation: Note that the program will consider all characters as data following the first space character in the input command. Observe the leading and trailing whitespaces for result of the second command in the above screenshot. ## List available modules: `list` From 303c982cb30219ae5629518b29f7da3ba3785d80 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 12 Oct 2021 22:04:13 +0800 Subject: [PATCH 176/607] Add requested changes --- docs/UserGuide.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 1521eaf52a..eef3d83937 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -16,6 +16,7 @@ This user guide is tailored for CTF players who have basic understanding of info - [Features](#features) - [List available modules: `list`](#list-available-modules-list) - [Selection of module: `select`](#selection-of-module-select) +- [Exiting the program: `exit`](#exiting-the-program-exit) - [Command Summary](#command-summary) # Terminologies @@ -61,10 +62,10 @@ Format: `exit` # Command Summary -| Action | Command Format | Example Usage | -|--------------------------|-------------------------|-----------------------------------------------| -| Enter input data | `data` `` | `data HelloWorld!`
`data SGVsbG9Xb3JsZA==` | -| List available modules | `list` | `list` | -| Select processing module | `select` `` | `select base64decode`
`select hexencode` | -| Reset to original data | `reset` | `reset` | -| Exit program | `exit` | `select hexencode` | \ No newline at end of file +| Action | Command Format | Example Usage | +|--------------------------|-----------------------|-------------------------------------------------| +| Enter input data | `input ` | `input hello world`
`input SGVsbG9Xb3JsZA==` | +| List available modules | `list` | `list` | +| Select processing module | `select ` | `select base64decode`
`select hexencode` | +| Reset to original data | `reset` | `reset` | +| Exit program | `exit` | `exit` | \ No newline at end of file From cbb09b1be08c0ca5d6f40cfb6d122306eb1a8a67 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 12 Oct 2021 22:36:12 +0800 Subject: [PATCH 177/607] Add an indent --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index eef3d83937..d3bd543d63 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -16,7 +16,7 @@ This user guide is tailored for CTF players who have basic understanding of info - [Features](#features) - [List available modules: `list`](#list-available-modules-list) - [Selection of module: `select`](#selection-of-module-select) -- [Exiting the program: `exit`](#exiting-the-program-exit) + - [Exiting the program: `exit`](#exiting-the-program-exit) - [Command Summary](#command-summary) # Terminologies From 8806d264160d03acedbc8977ebb8d5249e979f99 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 13 Oct 2021 01:15:33 +0800 Subject: [PATCH 178/607] Fix some formatting --- docs/UserGuide.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d3bd543d63..a3e9248819 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -62,10 +62,10 @@ Format: `exit` # Command Summary -| Action | Command Format | Example Usage | -|--------------------------|-----------------------|-------------------------------------------------| -| Enter input data | `input ` | `input hello world`
`input SGVsbG9Xb3JsZA==` | -| List available modules | `list` | `list` | -| Select processing module | `select ` | `select base64decode`
`select hexencode` | -| Reset to original data | `reset` | `reset` | -| Exit program | `exit` | `exit` | \ No newline at end of file +| Action | Command Format | Example Usage | +|--------------------------|-----------------------|--------------------------------------------------------------------| +| Enter input data | `input ` | `input SGVsbG9Xb3JsZA==`
input  hello world  | +| List available modules | `list` | `list` | +| Select processing module | `select ` | `select base64decode`
`select hexencode` | +| Reset to original data | `reset` | `reset` | +| Exit program | `exit` | `exit` | \ No newline at end of file From fbcc0c5ea818797de42f5228ac6c0d9239018d8d Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 13 Oct 2021 01:24:21 +0800 Subject: [PATCH 179/607] Update product name and description in `docs/README.md` --- docs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..9b875358f4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ -# Duke +# Decodex -{Give product intro here} +Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly transform data from one format to another with extreme ease**. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. Useful links: * [User Guide](UserGuide.md) From 6f16ec732e287c22f0bc99d61518df754da3daff Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 13 Oct 2021 01:34:14 +0800 Subject: [PATCH 180/607] Add Jemoji to github-pages --- docs/Gemfile | 3 +++ docs/_config.yml | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 docs/Gemfile diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 0000000000..481d48dc23 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem 'jemoji', '~> 0.12.0' \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml index c4192631f2..45fbb44e9f 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1 +1,3 @@ -theme: jekyll-theme-cayman \ No newline at end of file +theme: jekyll-theme-cayman +gems: + - jemoji \ No newline at end of file From da759b7aa14902e7c91f8d9b73616afc01d68833 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 13 Oct 2021 01:37:07 +0800 Subject: [PATCH 181/607] Add newline --- docs/Gemfile | 2 +- docs/_config.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Gemfile b/docs/Gemfile index 481d48dc23..7fbaf614ff 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -1,3 +1,3 @@ source "https://rubygems.org" -gem 'jemoji', '~> 0.12.0' \ No newline at end of file +gem 'jemoji', '~> 0.12.0' diff --git a/docs/_config.yml b/docs/_config.yml index 45fbb44e9f..059155b857 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,3 +1,3 @@ theme: jekyll-theme-cayman gems: - - jemoji \ No newline at end of file + - jemoji From a47dff1864c24b91ccb2d48d4a1a602cd9e3318e Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 13 Oct 2021 12:48:15 +0800 Subject: [PATCH 182/607] Add dimensions to profile pictures --- docs/AboutUs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 24a95c5ecb..dad9cf36f4 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,7 +2,7 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: -![](https://avatars.githubusercontent.com/u/26685970?v=4) | Tan Jia Le | [Github](https://github.com/rizemon) | [Portfolio](docs/team/johndoe.md) -![](https://avatars.githubusercontent.com/u/16810847?v=4) | Aloysius Lee | [Github](https://github.com/arraysius/) | [Portfolio](docs/team/johndoe.md) -![](https://media.discordapp.net/attachments/285692337357586432/892962858546114560/photo_2021-05-07_15-04-25.jpg?text=BreadDog) | Sean Phang | [Github](https://github.com/SeenFang) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Sim Tian Boon | [Github](https://github.com/Kair0s3/) | [Portfolio](docs/team/johndoe.md) + | Tan Jia Le | [Github](https://github.com/rizemon) | [Portfolio](docs/team/johndoe.md) + | Aloysius Lee | [Github](https://github.com/arraysius) | [Portfolio](docs/team/johndoe.md) + | Sean Phang | [Github](https://github.com/SeenFang) | [Portfolio](docs/team/johndoe.md) + | Sim Tian Boon | [Github](https://github.com/Kair0s3) | [Portfolio](docs/team/johndoe.md) From 37671daa2247caaf4c519434eec864cbe0eea02f Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:23:57 +0800 Subject: [PATCH 183/607] Add error status for invalid hex string message --- src/main/java/decodex/modules/hex/HexDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 3a14339511..45e580fb12 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -32,7 +32,7 @@ public Data run(Data data) throws ModuleException { String inputString = data.toString().toLowerCase(); if (!isValidHex(inputString)) { - throw new ModuleException("Invalid hexadecimal string"); + throw new ModuleException("[x] Invalid hexadecimal string"); } String decodedString = Arrays From f2429bf1ce0b88e4ef4d4ad002831e6f728acca6 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:30:51 +0800 Subject: [PATCH 184/607] Move exception message to `ModuleException`` --- src/main/java/decodex/data/exception/ModuleException.java | 4 ++-- src/main/java/decodex/modules/hex/HexDecoder.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/data/exception/ModuleException.java b/src/main/java/decodex/data/exception/ModuleException.java index 1d8653bc47..99eaf66119 100644 --- a/src/main/java/decodex/data/exception/ModuleException.java +++ b/src/main/java/decodex/data/exception/ModuleException.java @@ -5,8 +5,8 @@ */ public class ModuleException extends Exception { - public static final String BASE64_DECODING_FAILED_MESSAGE = "[x] " - + "Your data does not seem to be in Base64 format"; + public static final String BASE64_DECODING_FAILED_MESSAGE = "[x] Your data does not seem to be in Base64 format"; + public static final String HEX_DECODING_FAILED_MESSAGE = "[x] Invalid hexadecimal string"; public ModuleException(String message) { super(message); diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 45e580fb12..b507127e8d 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -32,7 +32,7 @@ public Data run(Data data) throws ModuleException { String inputString = data.toString().toLowerCase(); if (!isValidHex(inputString)) { - throw new ModuleException("[x] Invalid hexadecimal string"); + throw new ModuleException(ModuleException.HEX_DECODING_FAILED_MESSAGE); } String decodedString = Arrays From b82fe6ba3bf4bdea9ea080a73e9ed1e6d1ea7c1a Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 13 Oct 2021 14:01:30 +0800 Subject: [PATCH 185/607] Add comment about non-printable characters --- docs/UserGuide.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 48a3e03760..1d513a8427 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -48,7 +48,9 @@ Examples: ![carbon(1).png](images/carbon(1).png) -> :exclamation: Note that the program will consider all characters as data following the first space character in the input command. Observe the leading and trailing whitespaces for result of the second command in the above screenshot. +> :exclamation: Note that the program will consider all characters as data following the first space character in the input command. Observe the leading and trailing whitespaces in the result of the second command in the above screenshot. + +> :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. ## List available modules: `list` @@ -73,6 +75,8 @@ Examples: ![carbon(9).png](images/carbon(9).png) +> :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. + ## Resetting of data: `reset` Resets the transformed data back to the original input. From b407d74238794ea9a3a267900ded14501944b929 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:16:40 +0800 Subject: [PATCH 186/607] Fix user guide * Add missing terminologies * Remove repeated info * Add code block for `data` under `input` command --- docs/UserGuide.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 1d513a8427..86c55d0106 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -4,8 +4,6 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly transform data from one format to another with extreme ease**. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. -This user guide is tailored for CTF players who have basic understanding of information security concepts. - > :information_source: This user guide is tailored for CTF players who have basic understanding of information security concepts. ![carbon(6).png](images/carbon(6).png) @@ -25,11 +23,12 @@ This user guide is tailored for CTF players who have basic understanding of info This section serves to help the user better understand the terminologies used in this user guide. -| Terminology | Definition | -| ----------- | ---------- | -| | | -| | | - +| Terminology | Definition | +| ------------------- | ----------------------------------------------------------------------- | +| Encoding | Convert a message into a coded form. | +| Decoding | Convert a coded message into an intelligible form. | +| Base64, Hexadecimal | Common types of data encoding standards. | +| Module | A self-contained set of instructions to process data into another form. | # Features @@ -91,10 +90,10 @@ Format: `exit` # Command Summary -| Action | Command Format | Example Usage | -|--------------------------|-----------------------|--------------------------------------------------------------------| +| Action | Command Format | Example Usage | +| ------------------------ | --------------------- | ------------------------------------------------------------ | | Enter input data | `input ` | `input SGVsbG9Xb3JsZA==`
input  hello world  | -| List available modules | `list` | `list` | -| Select processing module | `select ` | `select base64decode`
`select hexencode` | -| Reset to original data | `reset` | `reset` | -| Exit program | `exit` | `exit` | \ No newline at end of file +| List available modules | `list` | `list` | +| Select processing module | `select ` | `select base64decode`
`select hexencode` | +| Reset to original data | `reset` | `reset` | +| Exit program | `exit` | `exit` | \ No newline at end of file From ed3b681af75d6ce7555db0e4839141399eccfca8 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:31:31 +0800 Subject: [PATCH 187/607] Fix code block for `data` --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 86c55d0106..456d85507f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -47,7 +47,7 @@ Examples: ![carbon(1).png](images/carbon(1).png) -> :exclamation: Note that the program will consider all characters as data following the first space character in the input command. Observe the leading and trailing whitespaces in the result of the second command in the above screenshot. +> :exclamation: Note that the program will consider all characters as `data` following the first space character in the input command. Observe the leading and trailing whitespaces in the result of the second command in the above screenshot. > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. From 21a25d889888b8d8a76718fb3258b91867d1ebee Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 19:35:15 +0800 Subject: [PATCH 188/607] Changes to standardized wording/convention standards in Parser.java *Additional change - Made changes to the return type of the respective crafting methods, to reduce ambiguity and more accuracy with the javadocs* --- src/main/java/decodex/parser/Parser.java | 52 ++++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 046e5ac2c3..2d5e05718f 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -21,7 +21,6 @@ public class Parser { private static final int COMMAND_INDEX = 0; private static final int MODULE_NAME_INDEX_IN_TOKENS = 0; - /** * Specifies the starting index where the arguments can be found. */ @@ -33,10 +32,11 @@ public class Parser { private static final int VALID_TOKENS_LENGTH_FOR_COMMAND = 1; /** - * Returns the type of command that user has entered. + * Returns the type of command that the user has specified. * * @param userInput The input specified by the user. - * @return The type of command. + * @return The type of the command. + * @throws ParserException If the user input is empty or missing the command type. */ public String getCommandType(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); @@ -58,10 +58,11 @@ public String getCommandType(String userInput) throws ParserException { } /** - * Returns the Module Name that user has specified. + * Returns the module name that the user has specified. * * @param userInput The input specified by the user. - * @return The Module name. + * @return The name of the module. + * @throws ParserException If the user input is missing the module name. */ public String getModuleName(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); @@ -77,10 +78,11 @@ public String getModuleName(String userInput) throws ParserException { } /** - * Returns the input data string that user has specified. + * Returns the InputCommand's argument string that the user has specified. * * @param userInput The input specified by the user. - * @return The input data string. + * @return The argument string. + * @throws ParserException If the argument string is empty. */ public String getInputString(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); @@ -95,11 +97,11 @@ public String getInputString(String userInput) throws ParserException { } /** - * Parses the user input provided by user and returns its respective command. Returns a null object if the command - * is invalid. + * Parses the user input specified by the user and returns its respective Command object. * * @param userInput The user input specified by the user. * @return The respective Command object. + * @throws ParserException If the command type is unknown/invalid. */ public Command parseCommand(String userInput) throws ParserException { Command command; @@ -128,52 +130,50 @@ public Command parseCommand(String userInput) throws ParserException { } /** - * Crafts and returns the Exit Command. + * Crafts and returns the ExitCommand object. * - * @return The Exit command. + * @return The ExitCommand object. */ - private Command craftExitCommand() { + private ExitCommand craftExitCommand() { return new ExitCommand(); } /** - * Crafts and returns the Data Command given the user input. + * Crafts and returns the InputCommand object using the specified user input. * * @param userInput The user input specified by the user. - * @return The Data command - * @throws ParserException ParserException + * @return The InputCommand object. */ - private Command craftInputCommand(String userInput) throws ParserException { + private InputCommand craftInputCommand(String userInput) throws ParserException { String inputData = getInputString(userInput); return new InputCommand(inputData); } /** - * Crafts and returns the List Command. + * Crafts and returns the ListCommand object. * - * @return The List command. + * @return The ListCommand object. */ - private Command craftListCommand() { + private ListCommand craftListCommand() { return new ListCommand(); } /** - * Crafts and returns the Reset Command. + * Crafts and returns the ResetCommand object. * - * @return The Reset command. + * @return The ResetCommand object. */ - private Command craftResetCommand() { + private ResetCommand craftResetCommand() { return new ResetCommand(); } /** - * Crafts and returns the Select Command. + * Crafts and returns the SelectCommand object using the specified user input. * * @param userInput The user input specified by the user. - * @return The Select command. - * @throws ParserException ParserException + * @return The SelectCommand object. */ - private Command craftSelectCommand(String userInput) throws ParserException { + private SelectCommand craftSelectCommand(String userInput) throws ParserException { String moduleName = getModuleName(userInput); return new SelectCommand(moduleName); } From 478e4e5d6dd72f024cee1d176fadac4e4ec357e2 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 19:38:04 +0800 Subject: [PATCH 189/607] Minor changes to some of the wordings in parser. --- src/main/java/decodex/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 2d5e05718f..2b3d1ff53a 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -58,7 +58,7 @@ public String getCommandType(String userInput) throws ParserException { } /** - * Returns the module name that the user has specified. + * Returns the name of the module that the user has specified. * * @param userInput The input specified by the user. * @return The name of the module. From 5b2ec1203d2b7189788ed6c98a118e8f6daa46c9 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 20:02:46 +0800 Subject: [PATCH 190/607] Added javadocs for DataManager --- src/main/java/decodex/data/DataManager.java | 28 ++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index eee8087be4..27d140b04c 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -15,11 +15,20 @@ public class DataManager { /* Data to perform the encoding/decoding operations on */ private Data currentData; + /** + * Constructor for DataManager. + */ public DataManager() { originalData = null; currentData = null; } + /** + * Returns the original Data object. + * + * @return The original Data object. + * @throws DataManagerException If the original Data object does not exist. + */ public Data getOriginalData() throws DataManagerException { if (originalData == null) { throw new DataManagerException("No data found"); @@ -27,6 +36,12 @@ public Data getOriginalData() throws DataManagerException { return originalData; } + /** + * Returns the current Data object. + * + * @return The current Data object. + * @throws DataManagerException If the current Data object does not exist. + */ public Data getCurrentData() throws DataManagerException { if (currentData == null) { throw new DataManagerException("No data found"); @@ -34,11 +49,22 @@ public Data getCurrentData() throws DataManagerException { return currentData; } + /** + * Sets the original Data given the original Data object. + * + * @param originalData The original Data object. + */ public void setOriginalData(Data originalData) { this.originalData = originalData; resetToOriginalData(); } + /** + * Sets the current Data given the new current Data object. + * + * @param currentData The new current Data Object. + * @throws DataManagerException If the original Data object does not exist. + */ public void setCurrentData(Data currentData) throws DataManagerException { if (originalData == null) { throw new DataManagerException("No data found"); @@ -47,7 +73,7 @@ public void setCurrentData(Data currentData) throws DataManagerException { } /** - * Undo all modifications done to the data. + * Undoes all modifications done to the data and resets it to its original data. */ public void resetToOriginalData() { currentData = originalData; From 3f16ca1418d9d450f02123a82029fd4b5e30ae94 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 20:05:17 +0800 Subject: [PATCH 191/607] Changes to the starting javadocs header of the class --- src/main/java/decodex/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 2b3d1ff53a..98014fcb26 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -11,7 +11,7 @@ import java.util.Arrays; /** - * Parses and validates the user input. + * The Parser class parses and validates the user input. */ public class Parser { From e12c1d8a8fd1fda59e8098f7f959b352d6dca213 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 16 Oct 2021 22:05:18 +0800 Subject: [PATCH 192/607] Add Rotational Cipher module --- .../java/decodex/modules/rot/RotEncoder.java | 92 +++++++++++++++++++ .../decodex/modules/rot/RotEncoderTest.java | 86 +++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 src/main/java/decodex/modules/rot/RotEncoder.java create mode 100644 src/test/java/decodex/modules/rot/RotEncoderTest.java diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java new file mode 100644 index 0000000000..d87de3fcf5 --- /dev/null +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -0,0 +1,92 @@ +package decodex.modules.rot; + +import decodex.data.Data; +import decodex.modules.Module; + +/** + * The RotEncoder class handles the rotation of the alphabets in the input string. + */ +public class RotEncoder extends Module { + + public static final String MODULE_NAME = "rotencode"; + public static final String MODULE_DESCRIPTION = "Rotates the alphabets in the input string by a specified amount"; + + private static final char LOWERCASE_A = 'a'; + private static final char LOWERCASE_Z = 'z'; + private static final char UPPERCASE_A = 'A'; + private static final char UPPERCASE_Z = 'Z'; + + private final int rotateOffset; + + public RotEncoder(int rotateOffset) { + super(MODULE_NAME, MODULE_DESCRIPTION); + this.rotateOffset = rotateOffset; + } + + /** + * Applies the rotation to the given Data object. + * + * @param data Data object to encode. + * @return new Data object representing the result. + */ + @Override + public Data run(Data data) { + StringBuilder encodedStringBuilder = new StringBuilder(); + + for (byte b : data.getRawBytes()) { + char updatedChar = (char) b; + if (isLowercaseAlphabet((char) b)) { + updatedChar = rotateLowercaseAlphabet((char) b, rotateOffset); + + } else if (isUppercaseAlphabet((char) b)) { + updatedChar = rotateUppercaseAlphabet((char) b, rotateOffset); + } + encodedStringBuilder.append(updatedChar); + } + + String encodedString = encodedStringBuilder.toString(); + return new Data(encodedString); + } + + /** + * Checks if the given character is a lowercase alphabet. + * + * @param c The given character to check. + * @return True if the character is a lowercase alphabet. + */ + private boolean isLowercaseAlphabet(char c) { + return LOWERCASE_A <= c && c <= LOWERCASE_Z; + } + + /** + * Checks if the given character is an uppercase alphabet. + * + * @param c The given character to check. + * @return True if the character is an uppercase alphabet. + */ + private boolean isUppercaseAlphabet(char c) { + return UPPERCASE_A <= c && c <= UPPERCASE_Z; + } + + /** + * Rotates the given lowercase alphabet. + * + * @param c The given lowercase alphabet. + * @param rotateOffset Number of positions to rotate the alphabet by. + * @return The updated alphabet. + */ + private char rotateLowercaseAlphabet(char c, int rotateOffset) { + return (char) (Math.floorMod(c - LOWERCASE_A + rotateOffset, 26) + LOWERCASE_A); + } + + /** + * Rotates the given uppercase alphabet. + * + * @param c The given uppercase alphabet. + * @param rotateOffset Number of positions to rotate the alphabet by. + * @return The updated alphabet. + */ + private char rotateUppercaseAlphabet(char c, int rotateOffset) { + return (char) (Math.floorMod(c - UPPERCASE_A + rotateOffset, 26) + UPPERCASE_A); + } +} diff --git a/src/test/java/decodex/modules/rot/RotEncoderTest.java b/src/test/java/decodex/modules/rot/RotEncoderTest.java new file mode 100644 index 0000000000..d39e7649bf --- /dev/null +++ b/src/test/java/decodex/modules/rot/RotEncoderTest.java @@ -0,0 +1,86 @@ +package decodex.modules.rot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +import decodex.data.Data; + +class RotEncoderTest { + + @Test + void run_smallPositiveOffset_rotatedData() { + Data testData = new Data("abc"); + Data expectedData = new Data("bcd"); + + RotEncoder rotEncoder = new RotEncoder(1); + Data resultData = rotEncoder.run(testData); + + assertEquals(resultData.toString(), expectedData.toString()); + } + + @Test + void run_bigPositiveOffset_rotatedAlphabets() { + Data testData = new Data("xyz"); + Data expectedData = new Data("qrs"); + + RotEncoder rotEncoder = new RotEncoder(45); + Data resultData = rotEncoder.run(testData); + + assertEquals(resultData.toString(), expectedData.toString()); + } + + @Test + void run_smallNegativeOffset_rotatedAlphabets() { + Data testData = new Data("abc"); + Data expectedData = new Data("zab"); + + RotEncoder rotEncoder = new RotEncoder(-1); + Data resultData = rotEncoder.run(testData); + + assertEquals(resultData.toString(), expectedData.toString()); + } + + @Test + void run_bigNegativeOffset_rotatedAlphabets() { + Data testData = new Data("xyz"); + Data expectedData = new Data("efg"); + + RotEncoder rotEncoder = new RotEncoder(-45); + Data resultData = rotEncoder.run(testData); + + assertEquals(resultData.toString(), expectedData.toString()); + } + + @Test + void run_onlyNonAlphabets_noChange() { + Data testData = new Data("123=="); + Data expectedData = new Data("123=="); + + RotEncoder rotEncoder = new RotEncoder(1); + Data resultData = rotEncoder.run(testData); + + assertEquals(resultData.toString(), expectedData.toString()); + } + + @Test + void run_emptyData_noChange() { + Data testData = new Data(""); + Data expectedData = new Data(""); + + RotEncoder rotEncoder = new RotEncoder(1); + Data resultData = rotEncoder.run(testData); + + assertEquals(resultData.toString(), expectedData.toString()); + } + + @Test + void run_nullBytes_noChange() { + Data testData = new Data("\00"); + Data expectedData = new Data("\00"); + + RotEncoder rotEncoder = new RotEncoder(1); + Data resultData = rotEncoder.run(testData); + + assertEquals(resultData.toString(), expectedData.toString()); + } +} \ No newline at end of file From 01d052839329d348d70d3c728f298e34957beb52 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 16 Oct 2021 22:10:58 +0800 Subject: [PATCH 193/607] Remove magic number --- src/main/java/decodex/modules/rot/RotEncoder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java index d87de3fcf5..7a6fd74a8b 100644 --- a/src/main/java/decodex/modules/rot/RotEncoder.java +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -15,6 +15,7 @@ public class RotEncoder extends Module { private static final char LOWERCASE_Z = 'z'; private static final char UPPERCASE_A = 'A'; private static final char UPPERCASE_Z = 'Z'; + private static final int ALPHABETS_COUNT = 26; private final int rotateOffset; @@ -76,7 +77,7 @@ private boolean isUppercaseAlphabet(char c) { * @return The updated alphabet. */ private char rotateLowercaseAlphabet(char c, int rotateOffset) { - return (char) (Math.floorMod(c - LOWERCASE_A + rotateOffset, 26) + LOWERCASE_A); + return (char) (Math.floorMod(c - LOWERCASE_A + rotateOffset, ALPHABETS_COUNT) + LOWERCASE_A); } /** @@ -87,6 +88,6 @@ private char rotateLowercaseAlphabet(char c, int rotateOffset) { * @return The updated alphabet. */ private char rotateUppercaseAlphabet(char c, int rotateOffset) { - return (char) (Math.floorMod(c - UPPERCASE_A + rotateOffset, 26) + UPPERCASE_A); + return (char) (Math.floorMod(c - UPPERCASE_A + rotateOffset, ALPHABETS_COUNT) + UPPERCASE_A); } } From 318c2a73687f0030a7e6eef9417d3035ae3fc038 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 23:09:11 +0800 Subject: [PATCH 194/607] Changes "Crafts" to "Prepares" instead --- src/main/java/decodex/parser/Parser.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 98014fcb26..cdeb6d0aae 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -130,7 +130,7 @@ public Command parseCommand(String userInput) throws ParserException { } /** - * Crafts and returns the ExitCommand object. + * Prepares and returns the ExitCommand object. * * @return The ExitCommand object. */ @@ -139,7 +139,7 @@ private ExitCommand craftExitCommand() { } /** - * Crafts and returns the InputCommand object using the specified user input. + * Prepares and returns the InputCommand object using the specified user input. * * @param userInput The user input specified by the user. * @return The InputCommand object. @@ -150,7 +150,7 @@ private InputCommand craftInputCommand(String userInput) throws ParserException } /** - * Crafts and returns the ListCommand object. + * Prepares and returns the ListCommand object. * * @return The ListCommand object. */ @@ -159,7 +159,7 @@ private ListCommand craftListCommand() { } /** - * Crafts and returns the ResetCommand object. + * Prepares and returns the ResetCommand object. * * @return The ResetCommand object. */ @@ -168,7 +168,7 @@ private ResetCommand craftResetCommand() { } /** - * Crafts and returns the SelectCommand object using the specified user input. + * Prepares and returns the SelectCommand object using the specified user input. * * @param userInput The user input specified by the user. * @return The SelectCommand object. From 88806ce2be99ea8946f631e48fff27e79cabaf3b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 23:13:07 +0800 Subject: [PATCH 195/607] Replace respective with "corresponding". Additional, also made changes to the "class javadoc headers" to follow a more fixed format as suggested. --- src/main/java/decodex/data/DataManager.java | 2 +- src/main/java/decodex/parser/Parser.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index 27d140b04c..a99d946b45 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -5,7 +5,7 @@ import decodex.data.exception.DataManagerException; /** - * The DataManager class manages the current state of the data. + * The DataManager class handles the management of the current state of the data. */ public class DataManager { diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index cdeb6d0aae..cb50f92f12 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -11,7 +11,7 @@ import java.util.Arrays; /** - * The Parser class parses and validates the user input. + * The Parser class handles the parsing and validation of the user input. */ public class Parser { @@ -100,7 +100,7 @@ public String getInputString(String userInput) throws ParserException { * Parses the user input specified by the user and returns its respective Command object. * * @param userInput The user input specified by the user. - * @return The respective Command object. + * @return The corresponding Command object. * @throws ParserException If the command type is unknown/invalid. */ public Command parseCommand(String userInput) throws ParserException { From 8cd8149dc7455157988f1ef40a72bcd83a6d12e2 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 23:14:13 +0800 Subject: [PATCH 196/607] Update to remove residing "respective" --- src/main/java/decodex/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index cb50f92f12..1c857cd331 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -97,7 +97,7 @@ public String getInputString(String userInput) throws ParserException { } /** - * Parses the user input specified by the user and returns its respective Command object. + * Parses the user input specified by the user and returns its corresponding Command object. * * @param userInput The user input specified by the user. * @return The corresponding Command object. From 7d4cb2a745da3b56663fefefb3cbd79e9a97fd31 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 23:17:48 +0800 Subject: [PATCH 197/607] Changing the constrcutor javadocs to use starting verb instead. --- src/main/java/decodex/data/DataManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index a99d946b45..bb1cb55a84 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -16,7 +16,7 @@ public class DataManager { private Data currentData; /** - * Constructor for DataManager. + * Instantiates a new DataManager. */ public DataManager() { originalData = null; From 0b26ba500b47c02f6f02b899301dd0267411dc39 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 23:21:14 +0800 Subject: [PATCH 198/607] Some of the changes suggested --- src/main/java/decodex/data/DataManager.java | 4 ++-- src/main/java/decodex/parser/Parser.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index bb1cb55a84..ed273faf57 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -50,7 +50,7 @@ public Data getCurrentData() throws DataManagerException { } /** - * Sets the original Data given the original Data object. + * Sets the original Data. * * @param originalData The original Data object. */ @@ -60,7 +60,7 @@ public void setOriginalData(Data originalData) { } /** - * Sets the current Data given the new current Data object. + * Sets the current Data. * * @param currentData The new current Data Object. * @throws DataManagerException If the original Data object does not exist. diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 1c857cd331..f893885b04 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -172,6 +172,7 @@ private ResetCommand craftResetCommand() { * * @param userInput The user input specified by the user. * @return The SelectCommand object. + * @throws ParserException If the user input is missing the module name. */ private SelectCommand craftSelectCommand(String userInput) throws ParserException { String moduleName = getModuleName(userInput); From 024ba5d63c33324736827ca3f05de9bb166f01e9 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 16 Oct 2021 23:24:03 +0800 Subject: [PATCH 199/607] Add requested changes * Update the Javadoc comment * Change to Character.isLowercase and isUppercase --- .../java/decodex/modules/rot/RotEncoder.java | 30 +++---------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java index 7a6fd74a8b..ff1cdb0f5d 100644 --- a/src/main/java/decodex/modules/rot/RotEncoder.java +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -12,9 +12,7 @@ public class RotEncoder extends Module { public static final String MODULE_DESCRIPTION = "Rotates the alphabets in the input string by a specified amount"; private static final char LOWERCASE_A = 'a'; - private static final char LOWERCASE_Z = 'z'; private static final char UPPERCASE_A = 'A'; - private static final char UPPERCASE_Z = 'Z'; private static final int ALPHABETS_COUNT = 26; private final int rotateOffset; @@ -27,8 +25,8 @@ public RotEncoder(int rotateOffset) { /** * Applies the rotation to the given Data object. * - * @param data Data object to encode. - * @return new Data object representing the result. + * @param data The given Data object. + * @return A Data object with its alphabetical characters rotated. */ @Override public Data run(Data data) { @@ -36,10 +34,10 @@ public Data run(Data data) { for (byte b : data.getRawBytes()) { char updatedChar = (char) b; - if (isLowercaseAlphabet((char) b)) { + if (Character.isLowerCase((char) b)) { updatedChar = rotateLowercaseAlphabet((char) b, rotateOffset); - } else if (isUppercaseAlphabet((char) b)) { + } else if (Character.isUpperCase((char) b)) { updatedChar = rotateUppercaseAlphabet((char) b, rotateOffset); } encodedStringBuilder.append(updatedChar); @@ -49,26 +47,6 @@ public Data run(Data data) { return new Data(encodedString); } - /** - * Checks if the given character is a lowercase alphabet. - * - * @param c The given character to check. - * @return True if the character is a lowercase alphabet. - */ - private boolean isLowercaseAlphabet(char c) { - return LOWERCASE_A <= c && c <= LOWERCASE_Z; - } - - /** - * Checks if the given character is an uppercase alphabet. - * - * @param c The given character to check. - * @return True if the character is an uppercase alphabet. - */ - private boolean isUppercaseAlphabet(char c) { - return UPPERCASE_A <= c && c <= UPPERCASE_Z; - } - /** * Rotates the given lowercase alphabet. * From c322a7e89ab373452159d287bd3c862127ae3867 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 16 Oct 2021 23:24:36 +0800 Subject: [PATCH 200/607] Updated the JavaDocs as of the suggestions 2.0 --- src/main/java/decodex/data/DataManager.java | 2 +- src/main/java/decodex/parser/Parser.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index ed273faf57..73eca217f3 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -73,7 +73,7 @@ public void setCurrentData(Data currentData) throws DataManagerException { } /** - * Undoes all modifications done to the data and resets it to its original data. + * Reverts the current data to its original data. */ public void resetToOriginalData() { currentData = originalData; diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index f893885b04..1500a4f6f1 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -97,11 +97,11 @@ public String getInputString(String userInput) throws ParserException { } /** - * Parses the user input specified by the user and returns its corresponding Command object. + * Parses the user input specified by the user and returns its corresponding Command. * * @param userInput The user input specified by the user. * @return The corresponding Command object. - * @throws ParserException If the command type is unknown/invalid. + * @throws ParserException If the command type is unknown or invalid. */ public Command parseCommand(String userInput) throws ParserException { Command command; @@ -130,7 +130,7 @@ public Command parseCommand(String userInput) throws ParserException { } /** - * Prepares and returns the ExitCommand object. + * Prepares and returns the ExitCommand. * * @return The ExitCommand object. */ @@ -139,7 +139,7 @@ private ExitCommand craftExitCommand() { } /** - * Prepares and returns the InputCommand object using the specified user input. + * Prepares and returns the InputCommand using the specified user input. * * @param userInput The user input specified by the user. * @return The InputCommand object. @@ -150,7 +150,7 @@ private InputCommand craftInputCommand(String userInput) throws ParserException } /** - * Prepares and returns the ListCommand object. + * Prepares and returns the ListCommand. * * @return The ListCommand object. */ @@ -159,7 +159,7 @@ private ListCommand craftListCommand() { } /** - * Prepares and returns the ResetCommand object. + * Prepares and returns the ResetCommand. * * @return The ResetCommand object. */ @@ -168,7 +168,7 @@ private ResetCommand craftResetCommand() { } /** - * Prepares and returns the SelectCommand object using the specified user input. + * Prepares and returns the SelectCommand using the specified user input. * * @param userInput The user input specified by the user. * @return The SelectCommand object. From a88a9f6a056346c98567b2967052d23443a534f6 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 16 Oct 2021 23:36:01 +0800 Subject: [PATCH 201/607] Break down complex expression --- src/main/java/decodex/modules/rot/RotEncoder.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java index ff1cdb0f5d..6f1330c245 100644 --- a/src/main/java/decodex/modules/rot/RotEncoder.java +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -55,7 +55,10 @@ public Data run(Data data) { * @return The updated alphabet. */ private char rotateLowercaseAlphabet(char c, int rotateOffset) { - return (char) (Math.floorMod(c - LOWERCASE_A + rotateOffset, ALPHABETS_COUNT) + LOWERCASE_A); + int originalAlphabetIndex = c - LOWERCASE_A; + int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + rotateOffset, ALPHABETS_COUNT); + + return (char) (LOWERCASE_A + updatedAlphabetIndex); } /** @@ -66,6 +69,9 @@ private char rotateLowercaseAlphabet(char c, int rotateOffset) { * @return The updated alphabet. */ private char rotateUppercaseAlphabet(char c, int rotateOffset) { - return (char) (Math.floorMod(c - UPPERCASE_A + rotateOffset, ALPHABETS_COUNT) + UPPERCASE_A); + int originalAlphabetIndex = c - UPPERCASE_A; + int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + rotateOffset, ALPHABETS_COUNT); + + return (char) (UPPERCASE_A + updatedAlphabetIndex); } } From cb1410a5d438d41faab2bf797109e09466ed7a60 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sun, 17 Oct 2021 00:09:44 +0800 Subject: [PATCH 202/607] Add requested changes * Remove some newlines * Add newline at EOF --- src/main/java/decodex/modules/rot/RotEncoder.java | 3 --- src/test/java/decodex/modules/rot/RotEncoderTest.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java index 6f1330c245..699f3fb489 100644 --- a/src/main/java/decodex/modules/rot/RotEncoder.java +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -36,7 +36,6 @@ public Data run(Data data) { char updatedChar = (char) b; if (Character.isLowerCase((char) b)) { updatedChar = rotateLowercaseAlphabet((char) b, rotateOffset); - } else if (Character.isUpperCase((char) b)) { updatedChar = rotateUppercaseAlphabet((char) b, rotateOffset); } @@ -57,7 +56,6 @@ public Data run(Data data) { private char rotateLowercaseAlphabet(char c, int rotateOffset) { int originalAlphabetIndex = c - LOWERCASE_A; int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + rotateOffset, ALPHABETS_COUNT); - return (char) (LOWERCASE_A + updatedAlphabetIndex); } @@ -71,7 +69,6 @@ private char rotateLowercaseAlphabet(char c, int rotateOffset) { private char rotateUppercaseAlphabet(char c, int rotateOffset) { int originalAlphabetIndex = c - UPPERCASE_A; int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + rotateOffset, ALPHABETS_COUNT); - return (char) (UPPERCASE_A + updatedAlphabetIndex); } } diff --git a/src/test/java/decodex/modules/rot/RotEncoderTest.java b/src/test/java/decodex/modules/rot/RotEncoderTest.java index d39e7649bf..d1829d12fd 100644 --- a/src/test/java/decodex/modules/rot/RotEncoderTest.java +++ b/src/test/java/decodex/modules/rot/RotEncoderTest.java @@ -83,4 +83,4 @@ void run_nullBytes_noChange() { assertEquals(resultData.toString(), expectedData.toString()); } -} \ No newline at end of file +} From ad573c2e3c2ad59c4fb0809ff539a6c6f01ca0d2 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 17 Oct 2021 02:08:20 +0800 Subject: [PATCH 203/607] Move all UI related functions to Ui class and package --- src/main/java/decodex/Decodex.java | 40 ++----- .../java/decodex/commands/ExitCommand.java | 2 +- .../java/decodex/commands/InputCommand.java | 7 +- .../java/decodex/commands/ListCommand.java | 6 +- .../java/decodex/commands/ResetCommand.java | 3 +- .../java/decodex/commands/SelectCommand.java | 5 +- src/main/java/decodex/data/Data.java | 4 +- src/main/java/decodex/data/DataManager.java | 15 +-- .../data/exception/ModuleException.java | 3 - .../data/exception/ParserException.java | 3 - .../decodex/modules/base64/Base64Decoder.java | 4 +- .../java/decodex/modules/hex/HexDecoder.java | 3 +- src/main/java/decodex/parser/Parser.java | 17 +-- src/main/java/decodex/ui/Ui.java | 101 ++++++++++++++++++ .../decodex/ui/messages/AssertMessages.java | 19 ++++ .../decodex/ui/messages/ErrorMessages.java | 13 +++ .../decodex/ui/messages/RegularMessages.java | 11 ++ .../decodex/modules/ModuleManagerTest.java | 1 - 18 files changed, 189 insertions(+), 68 deletions(-) create mode 100644 src/main/java/decodex/ui/messages/AssertMessages.java create mode 100644 src/main/java/decodex/ui/messages/ErrorMessages.java create mode 100644 src/main/java/decodex/ui/messages/RegularMessages.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 8c000e4326..c7c075198d 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -6,12 +6,12 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.ParserException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.parser.Parser; -import decodex.data.exception.ParserException; import decodex.ui.Ui; - +import decodex.ui.messages.AssertMessages; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; @@ -31,7 +31,6 @@ public class Decodex { private static Ui ui; public Decodex() { - printGreeting(); initDecodex(); } @@ -43,7 +42,6 @@ public static void initDecodex() { dataManager = new DataManager(); moduleManager = new ModuleManager(); parser = new Parser(); - in = new Scanner(System.in); ui = new Ui(); } @@ -55,46 +53,22 @@ public static void main(String[] args) { } public void run() { + ui.printGreeting(); + Command command = null; do { - printPromptHeader(); - String userInput = in.nextLine(); + String userInput = ui.readInput(); logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); - assert command != null : "Command should not be null"; + assert command != null : AssertMessages.COMMAND_NOT_NULL; command.run(dataManager, moduleManager, ui); } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { - printErrorMessage(err.getMessage()); + ui.printError(err); logger.fine(err.getMessage()); } } while (!(command instanceof ExitCommand)); } - - /** - * Prints the greeting message. - */ - public static void printGreeting() { - System.out.println("======================== Decodex ===========================\n" - + "Welcome to Decodex, transforming data with ease\n" - + "============================================================"); - } - - /** - * Prints the prompt header. - */ - public static void printPromptHeader() { - System.out.print("Decodex > "); - } - - /** - * Prints the handled error messages. - * - * @param errorMessage The respective error message. - */ - public static void printErrorMessage(String errorMessage) { - System.out.println(errorMessage); - } } diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java index 1394ae4d40..ffd4fe435c 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -14,6 +14,6 @@ public ExitCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { - System.out.print("Goodbye!"); + ui.printGoodbye(); } } diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index ca05198795..bd58f7adfb 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -5,6 +5,7 @@ import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; public class InputCommand extends Command { @@ -20,10 +21,12 @@ public InputCommand(String dataString) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws CommandException { if (dataString.isEmpty()) { - throw new CommandException("[-] Missing argument"); + throw new CommandException(ErrorMessages.MISSING_ARGUMENT); } + Data userData = new Data(dataString); dataManager.setOriginalData(userData); - System.out.printf("[+] Input: \"%s\"\n", dataString); + + ui.printInput(dataString); } } diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index cb50f2ba1d..4bf2de2b9b 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -4,6 +4,7 @@ import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.ui.Ui; +import decodex.ui.messages.AssertMessages; public class ListCommand extends Command { @@ -16,7 +17,7 @@ public ListCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { Module[] modules = moduleManager.getModules(); - assert modules.length > 0 : "Number of modules should be greater than 0"; + assert modules.length > 0 : AssertMessages.MODULES_GREATER_THAN_ZERO; StringBuilder moduleListString = new StringBuilder(); int maxNameWidth = 0; @@ -36,7 +37,6 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { moduleListString.append(String.format(" %-" + maxNameWidth + "s - %s\n", moduleName, moduleDescription)); } - System.out.println("Here are the list of supported modules:"); - System.out.println(moduleListString); + ui.printModuleList(moduleListString.toString()); } } diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java index e6086ac90f..46ccaa1d0e 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.ui.Ui; +import decodex.ui.messages.RegularMessages; public class ResetCommand extends Command { @@ -15,6 +16,6 @@ public ResetCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { dataManager.resetToOriginalData(); - System.out.println("[+] Reverted all changes"); + ui.printSuccess(RegularMessages.REVERTED_ALL_CHANGES); } } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index f0676b1d4e..6383e65c67 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -9,6 +9,7 @@ import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; public class SelectCommand extends Command { @@ -25,13 +26,13 @@ public SelectCommand(String moduleName) { public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws UnknownModuleException, CommandException, DataManagerException, ModuleException { if (moduleName.isBlank()) { - throw new CommandException("[-] Missing module name"); + throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); } Module module = moduleManager.selectModule(moduleName); Data newData = module.run(dataManager.getCurrentData()); dataManager.setCurrentData(newData); - System.out.printf("[+] Output: \"%s\"\n", newData.toString()); + ui.printOutput(newData.toString()); } } diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 2b24c246b9..77db454acf 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -1,5 +1,7 @@ package decodex.data; +import decodex.ui.messages.AssertMessages; + /** * The Data class manages the data that is to be encoded or decoded. */ @@ -12,7 +14,7 @@ public Data(byte[] rawBytes) { } public Data(String data) { - assert data != null : "Data string should not be null"; + assert data != null : AssertMessages.DATA_NOT_NULL; rawBytes = data.getBytes(); } diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index eee8087be4..cdc2505029 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -1,8 +1,9 @@ package decodex.data; -import java.util.Arrays; - import decodex.data.exception.DataManagerException; +import decodex.ui.messages.AssertMessages; +import decodex.ui.messages.ErrorMessages; +import java.util.Arrays; /** * The DataManager class manages the current state of the data. @@ -22,14 +23,14 @@ public DataManager() { public Data getOriginalData() throws DataManagerException { if (originalData == null) { - throw new DataManagerException("No data found"); + throw new DataManagerException(ErrorMessages.NO_DATA_FOUND); } return originalData; } public Data getCurrentData() throws DataManagerException { if (currentData == null) { - throw new DataManagerException("No data found"); + throw new DataManagerException(ErrorMessages.NO_DATA_FOUND); } return currentData; } @@ -41,7 +42,7 @@ public void setOriginalData(Data originalData) { public void setCurrentData(Data currentData) throws DataManagerException { if (originalData == null) { - throw new DataManagerException("No data found"); + throw new DataManagerException(ErrorMessages.NO_DATA_FOUND); } this.currentData = currentData; } @@ -51,7 +52,7 @@ public void setCurrentData(Data currentData) throws DataManagerException { */ public void resetToOriginalData() { currentData = originalData; - assert Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes()) : "Current data is same as " - + "original data"; + assert Arrays.equals(currentData.getRawBytes(), + originalData.getRawBytes()) : AssertMessages.CURRENT_DATA_SAME_AS_ORIGINAL_DATA; } } diff --git a/src/main/java/decodex/data/exception/ModuleException.java b/src/main/java/decodex/data/exception/ModuleException.java index 99eaf66119..90e41dcd08 100644 --- a/src/main/java/decodex/data/exception/ModuleException.java +++ b/src/main/java/decodex/data/exception/ModuleException.java @@ -5,9 +5,6 @@ */ public class ModuleException extends Exception { - public static final String BASE64_DECODING_FAILED_MESSAGE = "[x] Your data does not seem to be in Base64 format"; - public static final String HEX_DECODING_FAILED_MESSAGE = "[x] Invalid hexadecimal string"; - public ModuleException(String message) { super(message); } diff --git a/src/main/java/decodex/data/exception/ParserException.java b/src/main/java/decodex/data/exception/ParserException.java index 5330f927bc..b3d809723f 100644 --- a/src/main/java/decodex/data/exception/ParserException.java +++ b/src/main/java/decodex/data/exception/ParserException.java @@ -2,9 +2,6 @@ public class ParserException extends Exception { - public static final String MISSING_COMMAND_ARGS_MESSAGE = "[-] Your command arguments is empty"; - public static final String MISSING_COMMAND_TYPE_MESSAGE = "[-] Your command cannot be blank"; - public ParserException(String message) { super(message); } diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 7daba92311..f3e3e9aa87 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -3,7 +3,7 @@ import decodex.data.Data; import decodex.data.exception.ModuleException; import decodex.modules.Module; - +import decodex.ui.messages.ErrorMessages; import java.util.Base64; /** @@ -36,7 +36,7 @@ public Data run(Data data) throws ModuleException { decodedData = new Data(base64DecodedBytes); return decodedData; } catch (IllegalArgumentException err) { - throw new ModuleException(ModuleException.BASE64_DECODING_FAILED_MESSAGE); + throw new ModuleException(ErrorMessages.BASE64_DECODING_FAILED_MESSAGE); } } } diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index b507127e8d..2a01ba9576 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -3,6 +3,7 @@ import decodex.data.Data; import decodex.data.exception.ModuleException; import decodex.modules.Module; +import decodex.ui.messages.ErrorMessages; import java.util.Arrays; import java.util.stream.Collectors; @@ -32,7 +33,7 @@ public Data run(Data data) throws ModuleException { String inputString = data.toString().toLowerCase(); if (!isValidHex(inputString)) { - throw new ModuleException(ModuleException.HEX_DECODING_FAILED_MESSAGE); + throw new ModuleException(ErrorMessages.HEX_DECODING_FAILED_MESSAGE); } String decodedString = Arrays diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 046e5ac2c3..fd252c4336 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -1,13 +1,14 @@ package decodex.parser; import decodex.commands.Command; -import decodex.commands.InputCommand; import decodex.commands.ExitCommand; +import decodex.commands.InputCommand; import decodex.commands.ListCommand; import decodex.commands.ResetCommand; import decodex.commands.SelectCommand; import decodex.data.exception.ParserException; - +import decodex.ui.messages.AssertMessages; +import decodex.ui.messages.ErrorMessages; import java.util.Arrays; /** @@ -41,7 +42,7 @@ public class Parser { public String getCommandType(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); if (strippedUserInput.isEmpty()) { - throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); + throw new ParserException(ErrorMessages.MISSING_COMMAND_TYPE); } String[] tokens = strippedUserInput.split(" "); @@ -49,11 +50,11 @@ public String getCommandType(String userInput) throws ParserException { boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH_FOR_COMMAND; if (isInvalidTokensLength) { - throw new ParserException(ParserException.MISSING_COMMAND_TYPE_MESSAGE); + throw new ParserException(ErrorMessages.MISSING_COMMAND_TYPE); } String commandType = tokens[COMMAND_INDEX]; - assert !commandType.isEmpty() : "Command Type should not be empty"; + assert !commandType.isEmpty() : AssertMessages.COMMAND_TYPE_NOT_EMPTY; return commandType; } @@ -71,7 +72,7 @@ public String getModuleName(String userInput) throws ParserException { .filter(value -> !value.isBlank()) .toArray(size -> new String[size]); if (argumentTokens.length == 0) { - throw new ParserException("[-] Module name is missing"); + throw new ParserException(ErrorMessages.MISSING_MODULE_NAME); } return argumentTokens[MODULE_NAME_INDEX_IN_TOKENS]; } @@ -89,7 +90,7 @@ public String getInputString(String userInput) throws ParserException { String argumentString = String.join(" ", Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length)); if (argumentString.isEmpty()) { - throw new ParserException("[-] Your input is empty"); + throw new ParserException(ErrorMessages.INPUT_EMPTY); } return argumentString; } @@ -122,7 +123,7 @@ public Command parseCommand(String userInput) throws ParserException { command = craftSelectCommand(userInput); break; default: - throw new ParserException("[x] Unknown command, please enter a valid command"); + throw new ParserException(ErrorMessages.UNKNOWN_COMMAND); } return command; } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 84770c2cac..258aeb23ab 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -1,5 +1,106 @@ package decodex.ui; +import decodex.ui.messages.AssertMessages; +import decodex.ui.messages.RegularMessages; +import java.util.Scanner; + +/** + * The Ui class handles all user input and message output ot the console. + */ public class Ui { + private static final String PROMPT_HEADER = "Decodex > "; + private static final String SUCCESS_ICON = "[+]"; + private static final String ERROR_ICON = "[x]"; + private static final String INPUT_PREFIX = "Input:"; + private static final String OUTPUT_PREFIX = "Output:"; + + public Ui() { + } + + /** + * Prints the greeting message. + */ + public void printGreeting() { + System.out.println(RegularMessages.GREETING); + } + + /** + * Prints the goodbye message. + */ + public void printGoodbye() { + System.out.println(RegularMessages.GOODBYE); + } + + /** + * Reads user input from console. + * + * @return String of the user input. + */ + public String readInput() { + printPromptHeader(); + Scanner in = new Scanner(System.in); + return in.nextLine(); + } + + /** + * Prints the read user input. + * + * @param input The user input read. + */ + public void printInput(String input) { + assert input != null : AssertMessages.INPUT_NOT_NULL; + assert !input.isBlank() : AssertMessages.INPUT_NOT_EMPTY; + printSuccess(String.format("%s \"%s\"\n", INPUT_PREFIX, input)); + } + + /** + * Prints the output after data manipulation. + * + * @param output String output of the data. + */ + public void printOutput(String output) { + assert output != null : AssertMessages.OUTPUT_NOT_NULL; + printSuccess(String.format("%s \"%s\"\n", OUTPUT_PREFIX, output)); + } + + /** + * Prints the list of available modules. + * + * @param moduleList String of the list of available modules. + */ + public void printModuleList(String moduleList) { + assert moduleList != null : AssertMessages.MODULE_LIST_NOT_NULL; + assert !moduleList.isBlank() : AssertMessages.MODULE_LIST_NOT_EMPTY; + System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); + } + + /** + * Prints a message with a success status icon. + * + * @param message Success message + */ + public void printSuccess(String message) { + assert message != null : AssertMessages.MESSAGE_NOT_NULL; + assert !message.isBlank() : AssertMessages.MESSAGE_NOT_EMPTY; + System.out.println(SUCCESS_ICON + " " + message); + } + + /** + * Prints the error message from the thrown exception with an error icon. + * + * @param exception The thrown exception. + */ + public void printError(Exception exception) { + assert exception.getMessage() != null : AssertMessages.EXCEPTION_MESSAGE_NOT_NULL; + assert !exception.getMessage().isBlank() : AssertMessages.EXCEPTION_MESSAGE_NOT_EMPTY; + System.out.println(ERROR_ICON + " " + exception.getMessage()); + } + + /** + * Prints the prompt header. + */ + private void printPromptHeader() { + System.out.print(PROMPT_HEADER); + } } diff --git a/src/main/java/decodex/ui/messages/AssertMessages.java b/src/main/java/decodex/ui/messages/AssertMessages.java new file mode 100644 index 0000000000..69f113e078 --- /dev/null +++ b/src/main/java/decodex/ui/messages/AssertMessages.java @@ -0,0 +1,19 @@ +package decodex.ui.messages; + +public class AssertMessages { + + public static final String COMMAND_NOT_NULL = "Command should not be null"; + public static final String COMMAND_TYPE_NOT_EMPTY = "Command Type should not be empty"; + public static final String CURRENT_DATA_SAME_AS_ORIGINAL_DATA = "Current data is same as original data"; + public static final String DATA_NOT_NULL = "Data string should not be null"; + public static final String MODULES_GREATER_THAN_ZERO = "Number of modules should be greater than 0"; + public static final String MESSAGE_NOT_NULL = "Message should not be null"; + public static final String MESSAGE_NOT_EMPTY = "Message should not be empty"; + public static final String INPUT_NOT_NULL = "Input should not be null"; + public static final String INPUT_NOT_EMPTY = "Input should not be empty"; + public static final String OUTPUT_NOT_NULL = "Output should not be null"; + public static final String MODULE_LIST_NOT_NULL = "Module list should not be null"; + public static final String MODULE_LIST_NOT_EMPTY = "Module list should not be empty"; + public static final String EXCEPTION_MESSAGE_NOT_NULL = "Exception message should not be null"; + public static final String EXCEPTION_MESSAGE_NOT_EMPTY = "Exception message should not be empty"; +} diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java new file mode 100644 index 0000000000..e5bf5d4640 --- /dev/null +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -0,0 +1,13 @@ +package decodex.ui.messages; + +public class ErrorMessages { + + public static final String MISSING_ARGUMENT = "Missing argument"; + public static final String MISSING_COMMAND_TYPE = "Your command cannot be blank"; + public static final String MISSING_MODULE_NAME = "Missing module name"; + public static final String INPUT_EMPTY = "Your input is empty"; + public static final String UNKNOWN_COMMAND = "Unknown command, please enter a valid command"; + public static final String NO_DATA_FOUND = "No data found"; + public static final String BASE64_DECODING_FAILED_MESSAGE = "Your data does not seem to be in Base64 format"; + public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; +} diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java new file mode 100644 index 0000000000..67b6f47ad1 --- /dev/null +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -0,0 +1,11 @@ +package decodex.ui.messages; + +public class RegularMessages { + + public static final String GREETING = "======================== Decodex ===========================\n" + + "Welcome to Decodex, transforming data with ease\n" + + "============================================================"; + public static final String GOODBYE = "Goodbye!"; + public static final String LIST_MODULES = "Here are the list of supported modules:"; + public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; +} diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index 30b6aa48b1..915e8d540a 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import decodex.data.exception.UnknownModuleException; import decodex.modules.base64.Base64Encoder; From 9b7d2e24ad9b715e69fa9069cd864fbe406756da Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 17 Oct 2021 02:16:04 +0800 Subject: [PATCH 204/607] Last missing parserexception throws doc --- src/main/java/decodex/parser/Parser.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 1500a4f6f1..1e5e7e5ac5 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -143,6 +143,7 @@ private ExitCommand craftExitCommand() { * * @param userInput The user input specified by the user. * @return The InputCommand object. + * @throws ParserException If the argument string is empty. */ private InputCommand craftInputCommand(String userInput) throws ParserException { String inputData = getInputString(userInput); From 82fbc66aa391798bcf70162753d1323c68e95d49 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 17 Oct 2021 02:26:20 +0800 Subject: [PATCH 205/607] Fix incorrect test-ui-test output * Pass a scanner into Ui * Remove additional newline when printing input and output --- src/main/java/decodex/Decodex.java | 3 ++- src/main/java/decodex/ui/Ui.java | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index c7c075198d..c85b370263 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -42,6 +42,7 @@ public static void initDecodex() { dataManager = new DataManager(); moduleManager = new ModuleManager(); parser = new Parser(); + in = new Scanner(System.in); ui = new Ui(); } @@ -58,7 +59,7 @@ public void run() { Command command = null; do { - String userInput = ui.readInput(); + String userInput = ui.readInput(in); logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 258aeb23ab..83f65e0239 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -35,11 +35,11 @@ public void printGoodbye() { /** * Reads user input from console. * + * @param in Scanner object. * @return String of the user input. */ - public String readInput() { + public String readInput(Scanner in) { printPromptHeader(); - Scanner in = new Scanner(System.in); return in.nextLine(); } @@ -51,7 +51,7 @@ public String readInput() { public void printInput(String input) { assert input != null : AssertMessages.INPUT_NOT_NULL; assert !input.isBlank() : AssertMessages.INPUT_NOT_EMPTY; - printSuccess(String.format("%s \"%s\"\n", INPUT_PREFIX, input)); + printSuccess(String.format("%s \"%s\"", INPUT_PREFIX, input)); } /** @@ -61,7 +61,7 @@ public void printInput(String input) { */ public void printOutput(String output) { assert output != null : AssertMessages.OUTPUT_NOT_NULL; - printSuccess(String.format("%s \"%s\"\n", OUTPUT_PREFIX, output)); + printSuccess(String.format("%s \"%s\"", OUTPUT_PREFIX, output)); } /** From f03ab030528fcfe7d670ae22c2a13e25ecbff990 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 17 Oct 2021 02:29:19 +0800 Subject: [PATCH 206/607] Add newline to text-ui-test expected output --- text-ui-test/EXPECTED.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 5e82a9d0b2..b06bc82f03 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -9,4 +9,4 @@ Decodex > [+] Output: "I am a lab rat" Decodex > [+] Output: "4920616d2061206c616220726174" Decodex > [+] Reverted all changes Decodex > [x] Unknown command, please enter a valid command -Decodex > Goodbye! \ No newline at end of file +Decodex > Goodbye! From aae3c4dfd6703fdee80cf589ec29b881c047cc3f Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 17 Oct 2021 15:31:43 +0800 Subject: [PATCH 207/607] Remove AssertMessages class --- src/main/java/decodex/Decodex.java | 3 +-- .../java/decodex/commands/ListCommand.java | 3 +-- src/main/java/decodex/data/Data.java | 4 +--- src/main/java/decodex/data/DataManager.java | 3 +-- src/main/java/decodex/parser/Parser.java | 3 +-- src/main/java/decodex/ui/Ui.java | 19 +++++++++---------- .../decodex/ui/messages/AssertMessages.java | 19 ------------------- 7 files changed, 14 insertions(+), 40 deletions(-) delete mode 100644 src/main/java/decodex/ui/messages/AssertMessages.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index c85b370263..3113b35c25 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -11,7 +11,6 @@ import decodex.modules.ModuleManager; import decodex.parser.Parser; import decodex.ui.Ui; -import decodex.ui.messages.AssertMessages; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; @@ -63,7 +62,7 @@ public void run() { logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); - assert command != null : AssertMessages.COMMAND_NOT_NULL; + assert command != null : "Command should not be null"; command.run(dataManager, moduleManager, ui); } catch (ParserException | CommandException | UnknownModuleException | DataManagerException | ModuleException err) { diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 4bf2de2b9b..707ef8c0f4 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -4,7 +4,6 @@ import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.ui.Ui; -import decodex.ui.messages.AssertMessages; public class ListCommand extends Command { @@ -17,7 +16,7 @@ public ListCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { Module[] modules = moduleManager.getModules(); - assert modules.length > 0 : AssertMessages.MODULES_GREATER_THAN_ZERO; + assert modules.length > 0 : "Number of modules should be greater than 0"; StringBuilder moduleListString = new StringBuilder(); int maxNameWidth = 0; diff --git a/src/main/java/decodex/data/Data.java b/src/main/java/decodex/data/Data.java index 77db454acf..2b24c246b9 100644 --- a/src/main/java/decodex/data/Data.java +++ b/src/main/java/decodex/data/Data.java @@ -1,7 +1,5 @@ package decodex.data; -import decodex.ui.messages.AssertMessages; - /** * The Data class manages the data that is to be encoded or decoded. */ @@ -14,7 +12,7 @@ public Data(byte[] rawBytes) { } public Data(String data) { - assert data != null : AssertMessages.DATA_NOT_NULL; + assert data != null : "Data string should not be null"; rawBytes = data.getBytes(); } diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index cdc2505029..3e446af867 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -1,7 +1,6 @@ package decodex.data; import decodex.data.exception.DataManagerException; -import decodex.ui.messages.AssertMessages; import decodex.ui.messages.ErrorMessages; import java.util.Arrays; @@ -53,6 +52,6 @@ public void setCurrentData(Data currentData) throws DataManagerException { public void resetToOriginalData() { currentData = originalData; assert Arrays.equals(currentData.getRawBytes(), - originalData.getRawBytes()) : AssertMessages.CURRENT_DATA_SAME_AS_ORIGINAL_DATA; + originalData.getRawBytes()) : "Current data is same as original data"; } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index fd252c4336..ac7cefda55 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -7,7 +7,6 @@ import decodex.commands.ResetCommand; import decodex.commands.SelectCommand; import decodex.data.exception.ParserException; -import decodex.ui.messages.AssertMessages; import decodex.ui.messages.ErrorMessages; import java.util.Arrays; @@ -54,7 +53,7 @@ public String getCommandType(String userInput) throws ParserException { } String commandType = tokens[COMMAND_INDEX]; - assert !commandType.isEmpty() : AssertMessages.COMMAND_TYPE_NOT_EMPTY; + assert !commandType.isEmpty() : "Command Type should not be empty"; return commandType; } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 83f65e0239..e97cc52a37 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -1,6 +1,5 @@ package decodex.ui; -import decodex.ui.messages.AssertMessages; import decodex.ui.messages.RegularMessages; import java.util.Scanner; @@ -49,8 +48,8 @@ public String readInput(Scanner in) { * @param input The user input read. */ public void printInput(String input) { - assert input != null : AssertMessages.INPUT_NOT_NULL; - assert !input.isBlank() : AssertMessages.INPUT_NOT_EMPTY; + assert input != null : "Input should not be null"; + assert !input.isBlank() : "Input should not be empty"; printSuccess(String.format("%s \"%s\"", INPUT_PREFIX, input)); } @@ -60,7 +59,7 @@ public void printInput(String input) { * @param output String output of the data. */ public void printOutput(String output) { - assert output != null : AssertMessages.OUTPUT_NOT_NULL; + assert output != null : "Output should not be null"; printSuccess(String.format("%s \"%s\"", OUTPUT_PREFIX, output)); } @@ -70,8 +69,8 @@ public void printOutput(String output) { * @param moduleList String of the list of available modules. */ public void printModuleList(String moduleList) { - assert moduleList != null : AssertMessages.MODULE_LIST_NOT_NULL; - assert !moduleList.isBlank() : AssertMessages.MODULE_LIST_NOT_EMPTY; + assert moduleList != null : "Module list should not be null"; + assert !moduleList.isBlank() : "Module list should not be empty"; System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } @@ -81,8 +80,8 @@ public void printModuleList(String moduleList) { * @param message Success message */ public void printSuccess(String message) { - assert message != null : AssertMessages.MESSAGE_NOT_NULL; - assert !message.isBlank() : AssertMessages.MESSAGE_NOT_EMPTY; + assert message != null : "Message should not be null"; + assert !message.isBlank() : "Message should not be empty"; System.out.println(SUCCESS_ICON + " " + message); } @@ -92,8 +91,8 @@ public void printSuccess(String message) { * @param exception The thrown exception. */ public void printError(Exception exception) { - assert exception.getMessage() != null : AssertMessages.EXCEPTION_MESSAGE_NOT_NULL; - assert !exception.getMessage().isBlank() : AssertMessages.EXCEPTION_MESSAGE_NOT_EMPTY; + assert exception.getMessage() != null : "Exception message should not be null"; + assert !exception.getMessage().isBlank() : "Exception message should not be empty"; System.out.println(ERROR_ICON + " " + exception.getMessage()); } diff --git a/src/main/java/decodex/ui/messages/AssertMessages.java b/src/main/java/decodex/ui/messages/AssertMessages.java deleted file mode 100644 index 69f113e078..0000000000 --- a/src/main/java/decodex/ui/messages/AssertMessages.java +++ /dev/null @@ -1,19 +0,0 @@ -package decodex.ui.messages; - -public class AssertMessages { - - public static final String COMMAND_NOT_NULL = "Command should not be null"; - public static final String COMMAND_TYPE_NOT_EMPTY = "Command Type should not be empty"; - public static final String CURRENT_DATA_SAME_AS_ORIGINAL_DATA = "Current data is same as original data"; - public static final String DATA_NOT_NULL = "Data string should not be null"; - public static final String MODULES_GREATER_THAN_ZERO = "Number of modules should be greater than 0"; - public static final String MESSAGE_NOT_NULL = "Message should not be null"; - public static final String MESSAGE_NOT_EMPTY = "Message should not be empty"; - public static final String INPUT_NOT_NULL = "Input should not be null"; - public static final String INPUT_NOT_EMPTY = "Input should not be empty"; - public static final String OUTPUT_NOT_NULL = "Output should not be null"; - public static final String MODULE_LIST_NOT_NULL = "Module list should not be null"; - public static final String MODULE_LIST_NOT_EMPTY = "Module list should not be empty"; - public static final String EXCEPTION_MESSAGE_NOT_NULL = "Exception message should not be null"; - public static final String EXCEPTION_MESSAGE_NOT_EMPTY = "Exception message should not be empty"; -} From 48f3efee4bf5fb538ba6cfb04a75c0166c2af234 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 17 Oct 2021 15:34:30 +0800 Subject: [PATCH 208/607] Move scanner into Ui --- src/main/java/decodex/Decodex.java | 4 +--- src/main/java/decodex/ui/Ui.java | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 3113b35c25..472e765fe5 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -26,7 +26,6 @@ public class Decodex { private static DataManager dataManager; private static ModuleManager moduleManager; private static Parser parser; - private static Scanner in; private static Ui ui; public Decodex() { @@ -41,7 +40,6 @@ public static void initDecodex() { dataManager = new DataManager(); moduleManager = new ModuleManager(); parser = new Parser(); - in = new Scanner(System.in); ui = new Ui(); } @@ -58,7 +56,7 @@ public void run() { Command command = null; do { - String userInput = ui.readInput(in); + String userInput = ui.readInput(); logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index e97cc52a37..6043393dee 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -14,7 +14,10 @@ public class Ui { private static final String INPUT_PREFIX = "Input:"; private static final String OUTPUT_PREFIX = "Output:"; + private final Scanner in; + public Ui() { + in = new Scanner(System.in); } /** @@ -34,10 +37,9 @@ public void printGoodbye() { /** * Reads user input from console. * - * @param in Scanner object. * @return String of the user input. */ - public String readInput(Scanner in) { + public String readInput() { printPromptHeader(); return in.nextLine(); } From 050f0a71054a77427ff6dafa66b5aea5ed880a6e Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 17 Oct 2021 15:40:39 +0800 Subject: [PATCH 209/607] Revert line --- src/main/java/decodex/data/DataManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index 3e446af867..00c8cd9c83 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -51,7 +51,7 @@ public void setCurrentData(Data currentData) throws DataManagerException { */ public void resetToOriginalData() { currentData = originalData; - assert Arrays.equals(currentData.getRawBytes(), - originalData.getRawBytes()) : "Current data is same as original data"; + assert Arrays.equals(currentData.getRawBytes(), originalData.getRawBytes()) : "Current data is same as " + + "original data"; } } From f8a538254f9d38af12d25a9a13b7c0e9f82a34ae Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 17 Oct 2021 18:16:40 +0800 Subject: [PATCH 210/607] Add binary encoder and decoder with tests --- .../decodex/modules/binary/BinaryDecoder.java | 75 +++++++++++++++++++ .../decodex/modules/binary/BinaryEncoder.java | 51 +++++++++++++ .../modules/binary/BinaryDecoderTest.java | 54 +++++++++++++ .../modules/binary/BinaryEncoderTest.java | 32 ++++++++ 4 files changed, 212 insertions(+) create mode 100644 src/main/java/decodex/modules/binary/BinaryDecoder.java create mode 100644 src/main/java/decodex/modules/binary/BinaryEncoder.java create mode 100644 src/test/java/decodex/modules/binary/BinaryDecoderTest.java create mode 100644 src/test/java/decodex/modules/binary/BinaryEncoderTest.java diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java new file mode 100644 index 0000000000..cbd23a9033 --- /dev/null +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -0,0 +1,75 @@ +package decodex.modules.binary; + +import decodex.data.Data; +import decodex.data.exception.ModuleException; +import decodex.modules.Module; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * BinaryDecoder handles the binary decoding operations. + */ +public class BinaryDecoder extends Module { + + public static final String MODULE_NAME = "bindecode"; + public static final String MODULE_DESCRIPTION = "Decodes the data from binary."; + + public BinaryDecoder() { + super(MODULE_NAME, MODULE_DESCRIPTION); + } + + /** + * Performs binary decoding on the provided Data object. + * + * @param data The Data to be decoded. + * @return A Data object containing the binary decoded data. + */ + @Override + public Data run(Data data) throws ModuleException { + String binString = data.toString().trim(); + + if (!isValidBinary(binString)) { + throw new ModuleException("Invalid binary string"); + } + + binString = binString.replace(" ", ""); + String[] splitBinaryString = splitBinaryString(binString); + + byte[] newBytes = new byte[splitBinaryString.length]; + for (int i = 0; i < splitBinaryString.length; i++) { + newBytes[i] = Byte.parseByte(splitBinaryString[i], 2); + } + + return new Data(newBytes); + } + + /** + * Checks if the provided binary string is in valid format. + * + * @param binString Binary string to check if it is valid. + * @return True if provided binary string is valid. + */ + private boolean isValidBinary(String binString) { + Pattern validBinaryPattern = Pattern.compile("^([01]{8}[ ]*)*$"); + Matcher validBinaryMatcher = validBinaryPattern.matcher(binString); + return validBinaryMatcher.find(); + } + + /** + * Splits the binary string into a String array of 8 characters each. + * + * @param binString Binary string to be split. + * @return A String array of 8 character binary strings. + */ + private String[] splitBinaryString(String binString) { + assert isValidBinary(binString) : "Binary string should be valid"; + + String[] splitString = new String[binString.length() / 8]; + + for (int i = 0; i < binString.length(); i += 8) { + splitString[i / 8] = binString.substring(i, i + 8); + } + + return splitString; + } +} diff --git a/src/main/java/decodex/modules/binary/BinaryEncoder.java b/src/main/java/decodex/modules/binary/BinaryEncoder.java new file mode 100644 index 0000000000..3ca4fa30a1 --- /dev/null +++ b/src/main/java/decodex/modules/binary/BinaryEncoder.java @@ -0,0 +1,51 @@ +package decodex.modules.binary; + +import decodex.data.Data; +import decodex.modules.Module; + +/** + * BinaryEncoder handles the binary encoding operations. + */ +public class BinaryEncoder extends Module { + + public static final String MODULE_NAME = "binencode"; + public static final String MODULE_DESCRIPTION = "Encodes the data into binary."; + + public BinaryEncoder() { + super(MODULE_NAME, MODULE_DESCRIPTION); + } + + /** + * Performs binary encoding on the provided Data object. + * + * @param data The Data to be encoded. + * @return A Data object containing the binary encoded data. + */ + @Override + public Data run(Data data) { + StringBuilder encodedStringBuilder = new StringBuilder(); + + for (byte b : data.getRawBytes()) { + String binString = leftPad(Integer.toBinaryString(b)); + encodedStringBuilder.append(binString); + encodedStringBuilder.append(" "); + } + + String encodedString = encodedStringBuilder.toString().trim(); + return new Data(encodedString); + } + + /** + * Pads leading "0"s until the binary string is 8 characters long. + * + * @param binString Binary string to be padded. + * @return A padded binary string. + */ + private String leftPad(String binString) { + if (binString.length() < 8) { + return "0".repeat(8 - binString.length()) + binString; + } else { + return binString; + } + } +} diff --git a/src/test/java/decodex/modules/binary/BinaryDecoderTest.java b/src/test/java/decodex/modules/binary/BinaryDecoderTest.java new file mode 100644 index 0000000000..3e3fc78111 --- /dev/null +++ b/src/test/java/decodex/modules/binary/BinaryDecoderTest.java @@ -0,0 +1,54 @@ +package decodex.modules.binary; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.Data; +import decodex.data.exception.ModuleException; +import org.junit.jupiter.api.Test; + +public class BinaryDecoderTest { + + BinaryDecoder binaryDecoder = new BinaryDecoder(); + + @Test + public void run_binaryStringWithSpaces_asciiString() throws ModuleException { + Data data = new Data(" 01110100 01100101 01110011 01110100 "); + Data newData = binaryDecoder.run(data); + assertEquals(newData.toString(), "test"); + } + + @Test + public void run_binaryStringWithoutSpaces_asciiString() throws ModuleException { + Data data = new Data("01110100011001010111001101110100"); + Data newData = binaryDecoder.run(data); + assertEquals(newData.toString(), "test"); + } + + @Test + public void run_binaryStringMixSpaces_asciiString() throws ModuleException { + Data data = new Data(" 01110100 0110010101110011 01110100"); + Data newData = binaryDecoder.run(data); + assertEquals(newData.toString(), "test"); + } + + @Test + public void run_blankString_blankBinaryString() throws ModuleException { + Data data = new Data(""); + Data newData = binaryDecoder.run(data); + assertEquals(newData.toString(), ""); + } + + @Test + public void run_nullByteBinaryString_success() throws ModuleException { + Data data = new Data("00000000"); + Data newData = binaryDecoder.run(data); + assertEquals(newData.toString(), "\00"); + } + + @Test + public void run_invalidBinaryString_moduleExceptionThrown() { + Data data = new Data("01110100 1110100"); + assertThrows(ModuleException.class, () -> binaryDecoder.run(data)); + } +} diff --git a/src/test/java/decodex/modules/binary/BinaryEncoderTest.java b/src/test/java/decodex/modules/binary/BinaryEncoderTest.java new file mode 100644 index 0000000000..9c79317083 --- /dev/null +++ b/src/test/java/decodex/modules/binary/BinaryEncoderTest.java @@ -0,0 +1,32 @@ +package decodex.modules.binary; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import decodex.data.Data; +import org.junit.jupiter.api.Test; + +public class BinaryEncoderTest { + + BinaryEncoder binaryEncoder = new BinaryEncoder(); + + @Test + public void run_asciiString_binaryString() { + Data data = new Data("test"); + Data newData = binaryEncoder.run(data); + assertEquals(newData.toString(), "01110100 01100101 01110011 01110100"); + } + + @Test + public void run_blankString_blankBinaryString() { + Data data = new Data(""); + Data newData = binaryEncoder.run(data); + assertEquals(newData.toString(), ""); + } + + @Test + public void run_nullByteString_success() { + Data data = new Data("\00"); + Data newData = binaryEncoder.run(data); + assertEquals(newData.toString(), "00000000"); + } +} From 6863a96783f59b6e6ff9dc76d37b10da35e71c8c Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sun, 17 Oct 2021 23:52:18 +0800 Subject: [PATCH 211/607] Add recipe class - Add recipe class - Add RecipeException --- .../data/exception/RecipeException.java | 13 +++ src/main/java/decodex/recipes/Recipe.java | 67 ++++++++++++++++ src/test/java/decodex/recipes/RecipeTest.java | 80 +++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 src/main/java/decodex/data/exception/RecipeException.java create mode 100644 src/main/java/decodex/recipes/Recipe.java create mode 100644 src/test/java/decodex/recipes/RecipeTest.java diff --git a/src/main/java/decodex/data/exception/RecipeException.java b/src/main/java/decodex/data/exception/RecipeException.java new file mode 100644 index 0000000000..aee8287bac --- /dev/null +++ b/src/main/java/decodex/data/exception/RecipeException.java @@ -0,0 +1,13 @@ +package decodex.data.exception; + +/** + * This exception is raised when an error during recipe execution occurs. + */ +public class RecipeException extends Exception { + + public static final String EMPTY_RECIPE_MESSAGE = "[x] There is nothing to be removed."; + + public RecipeException(String message) { + super(message); + } +} diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java new file mode 100644 index 0000000000..45eb8f90d7 --- /dev/null +++ b/src/main/java/decodex/recipes/Recipe.java @@ -0,0 +1,67 @@ +package decodex.recipes; + +import decodex.Decodex; +import decodex.data.exception.RecipeException; +import decodex.modules.Module; +import java.util.ArrayList; +import java.util.logging.Logger; + +/** + * The Recipe class manages a list of Modules to be run sequentially. + */ +public class Recipe { + + private Logger logger = Decodex.logger; + + private String name; + private ArrayList moduleList = new ArrayList<>(); + + public Recipe(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ArrayList getModuleList() { + return moduleList; + } + + /** + * Appends a new module to the end of the module list. + * + * @param module Module to be added to the recipe. + */ + public void push(Module module) { + moduleList.add(module); + logger.fine(String.format("[Recipe %s] Added module %s", name, module.getName())); + } + + /** + * Removes the latest module that was added to the list. + * + * @return Module that was popped from the list + */ + public Module pop() throws RecipeException { + if (moduleList.size() <= 0) { + throw new RecipeException(RecipeException.EMPTY_RECIPE_MESSAGE); + } + + Module removedModule = moduleList.remove(moduleList.size() - 1); + logger.fine(String.format("[Recipe %s] Removed module %s", name, removedModule.getName())); + return removedModule; + } + + /** + * Completely removes all the modules from the recipe. + */ + public void reset() { + moduleList.clear(); + logger.fine(String.format("[Recipe %s] Cleared", name)); + } +} diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java new file mode 100644 index 0000000000..566cf6e8aa --- /dev/null +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -0,0 +1,80 @@ +package decodex.recipes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.exception.RecipeException; +import decodex.modules.Module; +import decodex.modules.base64.Base64Decoder; +import decodex.modules.base64.Base64Encoder; +import decodex.modules.hex.HexDecoder; +import decodex.modules.hex.HexEncoder; +import org.junit.jupiter.api.Test; + +class RecipeTest { + + @Test + void getName_recipeName() { + Recipe recipe = new Recipe("testRecipe"); + String expectedName = "testRecipe"; + + assertEquals(recipe.getName(), expectedName); + } + + @Test + void setName_validName_newRecipeName() { + Recipe recipe = new Recipe("testRecipe"); + String newName = "chocolate pancakes"; + recipe.setName(newName); + + assertEquals(recipe.getName(), newName); + } + + @Test + void getModuleList_newRecipe_returnsNotNull() { + Recipe recipe = new Recipe("testRecipe"); + assertNotNull(recipe.getModuleList()); + } + + @Test + void push_singleModule_recipeContainsOneModule() { + Recipe recipe = new Recipe("testRecipe"); + Module module = new HexEncoder(); + recipe.push(module); + + assertEquals(recipe.getModuleList().size(), 1); + } + + @Test + void pop_singleModule_latestModule() throws RecipeException { + Recipe recipe = new Recipe("testRecipe"); + Module module = new HexDecoder(); + recipe.push(module); + + Module poppedModule = recipe.pop(); + + assertEquals(poppedModule, poppedModule); + } + + @Test + void pop_emptyRecipe_expectException() { + Recipe recipe = new Recipe("testRecipe"); + + assertThrows(RecipeException.class, () -> recipe.pop()); + } + + @Test + void reset_emptyModuleList() { + Recipe recipe = new Recipe("testRecipe"); + recipe.push(new HexEncoder()); + recipe.push(new HexDecoder()); + recipe.push(new Base64Encoder()); + recipe.push(new Base64Decoder()); + + recipe.reset(); + + assertEquals(recipe.getModuleList().size(), 0); + } + +} From 63d6e3573d6d51a673e1ee4c1ef855c356f687a3 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 18 Oct 2021 00:36:45 +0800 Subject: [PATCH 212/607] Add recipe manager --- .../exception/RecipeManagerException.java | 14 ++++ src/main/java/decodex/recipes/Recipe.java | 17 +++++ .../java/decodex/recipes/RecipeManager.java | 73 +++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/main/java/decodex/data/exception/RecipeManagerException.java create mode 100644 src/main/java/decodex/recipes/Recipe.java create mode 100644 src/main/java/decodex/recipes/RecipeManager.java diff --git a/src/main/java/decodex/data/exception/RecipeManagerException.java b/src/main/java/decodex/data/exception/RecipeManagerException.java new file mode 100644 index 0000000000..72471223f8 --- /dev/null +++ b/src/main/java/decodex/data/exception/RecipeManagerException.java @@ -0,0 +1,14 @@ +package decodex.data.exception; + +/** + * This exception is raised when an error during recipe manager execution occurs. + */ +public class RecipeManagerException extends Exception { + + public static final String EMPTY_RECIPELIST_MESSAGE = "[x] There is nothing to be removed."; + public static final String RECIPIE_NOT_FOUND_MESSAGE = "[x] Given recipe could not be found."; + + public RecipeManagerException(String message) { + super(message); + } +} diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java new file mode 100644 index 0000000000..cbdaaf7e1d --- /dev/null +++ b/src/main/java/decodex/recipes/Recipe.java @@ -0,0 +1,17 @@ +package decodex.recipes; + +/** + * !!! OVERWRITE THIS FILE !!!. + */ +public class Recipe { + + private String name; + + public Recipe(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java new file mode 100644 index 0000000000..9dd9ce4053 --- /dev/null +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -0,0 +1,73 @@ +package decodex.recipes; + +import decodex.Decodex; +import decodex.data.exception.RecipeManagerException; +import java.util.ArrayList; +import java.util.logging.Logger; + +/** + * RecipeManager manages a list of all recipes that were created/loaded. + */ +public class RecipeManager { + + private Logger logger = Decodex.logger; + + ArrayList recipeList; + + /** + * Creates a new RecipeManager with no recipes. + */ + public RecipeManager() { + this.recipeList = new ArrayList<>(); + } + + /** + * Creates a new RecipeManager with a predefined list of recipes. + * + * @param recipeList list of recipes to be loaded. + */ + public RecipeManager(ArrayList recipeList) { + this.recipeList = recipeList; + } + + /** + * Adds a recipe to recipe manager to be managed. + * + * @param recipe Recipe to be added to the recipe manager. + */ + public void addRecipe(Recipe recipe) { + recipeList.add(recipe); + logger.fine(String.format("[RecipeManager] Added recipe %s", recipe.getName())); + } + + /** + * Removes a recipe from the recipe manager. + * + * @param name Name of the recipe to be removed. + * @return Recipe that was removed. + * @throws RecipeManagerException Recipe could not be found in the recipe manager. + */ + public Recipe removeRecipe(String name) throws RecipeManagerException { + Recipe recipeToRemove = getRecipe(name); + recipeList.remove(recipeToRemove); + logger.fine(String.format("[RecipeManager] Removed recipe %s", recipeToRemove.getName())); + return recipeToRemove; + } + + /** + * Get a Recipe object from the recipe manager by name. + * + * @param name Name of the recipe to get. + * @return Recipe that was found. + * @throws RecipeManagerException Recipe could not be found in the recipe manager. + */ + public Recipe getRecipe(String name) throws RecipeManagerException { + for (Recipe recipe : recipeList) { + if (recipe.getName().equals(name)) { + return recipe; + } + } + throw new RecipeManagerException(RecipeManagerException.RECIPIE_NOT_FOUND_MESSAGE); + } + +} From 80fd52cb24d51c0f8a6a94e3c9162859340c33d8 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 18 Oct 2021 20:17:07 +0800 Subject: [PATCH 213/607] Changes to Storage class for file IO to read in from default file (possibly add another function to read from user specified file instead) and output to a generated output file using datetime --- input/input.txt | 1 + output/2021-18-10__20.13.32.txt | 1 + .../decodex/data/exception/FileException.java | 15 +++ src/main/java/decodex/storage/Storage.java | 127 ++++++++++++++++++ .../java/decodex/storage/StorageTest.java | 33 +++++ 5 files changed, 177 insertions(+) create mode 100644 input/input.txt create mode 100644 output/2021-18-10__20.13.32.txt create mode 100644 src/main/java/decodex/data/exception/FileException.java create mode 100644 src/test/java/decodex/storage/StorageTest.java diff --git a/input/input.txt b/input/input.txt new file mode 100644 index 0000000000..cef5fdd809 --- /dev/null +++ b/input/input.txt @@ -0,0 +1 @@ +TESTING FOR JUNIT diff --git a/output/2021-18-10__20.13.32.txt b/output/2021-18-10__20.13.32.txt new file mode 100644 index 0000000000..d785a25b5e --- /dev/null +++ b/output/2021-18-10__20.13.32.txt @@ -0,0 +1 @@ +Hi I am a Test! diff --git a/src/main/java/decodex/data/exception/FileException.java b/src/main/java/decodex/data/exception/FileException.java new file mode 100644 index 0000000000..99589b9ce2 --- /dev/null +++ b/src/main/java/decodex/data/exception/FileException.java @@ -0,0 +1,15 @@ +package decodex.data.exception; + +public class FileException extends Exception { + + public static final String DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE = + "[x] The default input file does not exists"; + public static final String FILE_CREATION_ERROR_MESSAGE = + "[x] Something went wrong while reading from default input file"; + + public static final String FILE_WRITE_ERROR_MESSAGE = "[x] Something went wrong while writing to output file"; + + public FileException(String message) { + super(message); + } +} diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 7f1902b054..4676750703 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -1,5 +1,132 @@ package decodex.storage; +import decodex.data.exception.FileException; + +import java.io.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Scanner; + public class Storage { + /** + * Variables for loading from the default input filepath. + */ + public static final String DEFAULT_INPUT_DIRECTORY = "input"; + public static final String DEFAULT_INPUT_FILE = "input.txt"; + + /** + * Variable for the default output directory. + */ + public static final String DEFAULT_OUTPUT_DIRECTORY = "output"; + + /** + * Initializes a new Storage. + */ + public Storage() throws FileException { + instantiateDirectories(); + instantiateInputFile(); + } + + /** + * Reads the contents from the default input file. + * + * @return The contents from the default input file. + * @throws FileException If the default input file does not exist. + */ + public String readFromDefaultInputFile() throws FileException { + String inputContent = ""; + File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); + File inputFile = new File(inputDirectory, DEFAULT_INPUT_FILE); + + try { + Scanner in = new Scanner(inputFile); + while (in.hasNext()) { + String fileLine = in.nextLine(); + inputContent = inputContent + fileLine; + } + } catch (FileNotFoundException e) { + throw new FileException(FileException.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); + } + return inputContent; + } + + /** + * Writes the given output into a file. + * + * @param output The encoded or decoded output. + * @throws FileException If an I/O exception is caught when creating the output file + * or when writing to the output file. + */ + public void writeOutputToFile(String output) throws FileException { + LocalDateTime currentDateTime = LocalDateTime.now(); + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-dd-MM__HH.mm.ss"); + String formattedDateTime = currentDateTime.format(dateTimeFormatter); + String newOutputFileName = formattedDateTime + ".txt"; + + File outputDirectory = new File(DEFAULT_OUTPUT_DIRECTORY); + File outputFile = new File(outputDirectory, newOutputFileName); + + try { + outputFile.createNewFile(); + } catch (IOException e) { + throw new FileException(FileException.FILE_CREATION_ERROR_MESSAGE); + } + + try { + FileWriter fw = new FileWriter(outputFile); + fw.write(output + "\n"); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Instantiates the necessary directories if they do not exist yet. + */ + private void instantiateDirectories() { + instantiateInputDirectory(); + instantiateOutputDirectory(); + } + + /** + * Instantiates the default input directory. + */ + private void instantiateInputDirectory() { + File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); + if (!inputDirectory.exists()) { + inputDirectory.mkdir(); + } + } + + /** + * Instantiates the default output directory. + */ + private void instantiateOutputDirectory() { + File outputDirectory = new File(DEFAULT_OUTPUT_DIRECTORY); + if (!outputDirectory.exists()) { + outputDirectory.mkdir(); + } + } + + /** + * Instantiates the default input file. + * + * @throws FileException If an I/O exception is caught when creating the file. + */ + private void instantiateInputFile() throws FileException { + File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); + File inputFile = new File(inputDirectory, DEFAULT_INPUT_FILE); + + if (inputFile.exists()) { + return; + } + + try { + inputFile.createNewFile(); + } catch (IOException e) { + throw new FileException(FileException.FILE_CREATION_ERROR_MESSAGE); + } + } } diff --git a/src/test/java/decodex/storage/StorageTest.java b/src/test/java/decodex/storage/StorageTest.java new file mode 100644 index 0000000000..d8fe32e922 --- /dev/null +++ b/src/test/java/decodex/storage/StorageTest.java @@ -0,0 +1,33 @@ +package decodex.storage; + +import decodex.data.exception.FileException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class StorageTest { + + @Test + void readFromDefaultInputFile_emptyInputFile_emptyString() throws FileException { + Storage storage = new Storage(); + String inputContent = storage.readFromDefaultInputFile(); + + assertEquals("", inputContent); + } + + @Test + void readFromDefaultInputFile_inputFileWithSomeContent_stringContent() throws FileException { + Storage storage = new Storage(); + String inputContent = storage.readFromDefaultInputFile(); + + assertEquals("TESTING FOR JUNIT", inputContent); + } + + @Test + void writeOutputToFile_decodedOrEncodedString_successfulWriteToFile() throws FileException { + Storage storage = new Storage(); + String testStringToBeWritten = "Hi I am a Test!"; + + storage.writeOutputToFile(testStringToBeWritten); + } +} From ae8dea47a7c5b61c35350bf344fb08c8ee40e646 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 18 Oct 2021 20:22:26 +0800 Subject: [PATCH 214/607] Updated to fix the automatically added wildcard imports --- src/main/java/decodex/storage/Storage.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 4676750703..7187e000fd 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -2,7 +2,10 @@ import decodex.data.exception.FileException; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Scanner; From 4d0e4a3d7a9812332894c213878c01ab55f8c756 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 18 Oct 2021 20:43:07 +0800 Subject: [PATCH 215/607] Removed one of the JUnit test for the empty content file, Im thinking I might need to remove all the JUnit for File IO --- src/test/java/decodex/storage/StorageTest.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/test/java/decodex/storage/StorageTest.java b/src/test/java/decodex/storage/StorageTest.java index d8fe32e922..c7b52c8556 100644 --- a/src/test/java/decodex/storage/StorageTest.java +++ b/src/test/java/decodex/storage/StorageTest.java @@ -6,15 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; class StorageTest { - - @Test - void readFromDefaultInputFile_emptyInputFile_emptyString() throws FileException { - Storage storage = new Storage(); - String inputContent = storage.readFromDefaultInputFile(); - - assertEquals("", inputContent); - } - + @Test void readFromDefaultInputFile_inputFileWithSomeContent_stringContent() throws FileException { Storage storage = new Storage(); From efa0bfa9679410d2ad9f8364e7d61682c01beb32 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 18 Oct 2021 23:52:54 +0800 Subject: [PATCH 216/607] Add requested changes --- .../data/exception/RecipeException.java | 2 +- src/main/java/decodex/recipes/Recipe.java | 7 +++-- src/test/java/decodex/recipes/RecipeTest.java | 29 ++----------------- 3 files changed, 8 insertions(+), 30 deletions(-) diff --git a/src/main/java/decodex/data/exception/RecipeException.java b/src/main/java/decodex/data/exception/RecipeException.java index aee8287bac..2e815e25e6 100644 --- a/src/main/java/decodex/data/exception/RecipeException.java +++ b/src/main/java/decodex/data/exception/RecipeException.java @@ -5,7 +5,7 @@ */ public class RecipeException extends Exception { - public static final String EMPTY_RECIPE_MESSAGE = "[x] There is nothing to be removed."; + public static final String EMPTY_RECIPE_MESSAGE = "[x] The recipe is currently empty. There is no module to be removed."; public RecipeException(String message) { super(message); diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index 45eb8f90d7..ff100b047f 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -1,10 +1,11 @@ package decodex.recipes; +import java.util.ArrayList; +import java.util.logging.Logger; + import decodex.Decodex; import decodex.data.exception.RecipeException; import decodex.modules.Module; -import java.util.ArrayList; -import java.util.logging.Logger; /** * The Recipe class manages a list of Modules to be run sequentially. @@ -48,7 +49,7 @@ public void push(Module module) { * @return Module that was popped from the list */ public Module pop() throws RecipeException { - if (moduleList.size() <= 0) { + if (moduleList.isEmpty()) { throw new RecipeException(RecipeException.EMPTY_RECIPE_MESSAGE); } diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index 566cf6e8aa..691c5efb08 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -1,42 +1,19 @@ package decodex.recipes; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import decodex.data.exception.RecipeException; -import decodex.modules.Module; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; +import decodex.modules.Module; import org.junit.jupiter.api.Test; class RecipeTest { - @Test - void getName_recipeName() { - Recipe recipe = new Recipe("testRecipe"); - String expectedName = "testRecipe"; - - assertEquals(recipe.getName(), expectedName); - } - - @Test - void setName_validName_newRecipeName() { - Recipe recipe = new Recipe("testRecipe"); - String newName = "chocolate pancakes"; - recipe.setName(newName); - - assertEquals(recipe.getName(), newName); - } - - @Test - void getModuleList_newRecipe_returnsNotNull() { - Recipe recipe = new Recipe("testRecipe"); - assertNotNull(recipe.getModuleList()); - } - @Test void push_singleModule_recipeContainsOneModule() { Recipe recipe = new Recipe("testRecipe"); @@ -74,7 +51,7 @@ void reset_emptyModuleList() { recipe.reset(); - assertEquals(recipe.getModuleList().size(), 0); + assertTrue(recipe.getModuleList().isEmpty()); } } From 7350564a79263d63dca354d1b8c997a07ee992c0 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Mon, 18 Oct 2021 23:54:06 +0800 Subject: [PATCH 217/607] Fix line length --- src/main/java/decodex/data/exception/RecipeException.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/data/exception/RecipeException.java b/src/main/java/decodex/data/exception/RecipeException.java index 2e815e25e6..cd2c50f4de 100644 --- a/src/main/java/decodex/data/exception/RecipeException.java +++ b/src/main/java/decodex/data/exception/RecipeException.java @@ -5,7 +5,8 @@ */ public class RecipeException extends Exception { - public static final String EMPTY_RECIPE_MESSAGE = "[x] The recipe is currently empty. There is no module to be removed."; + public static final String EMPTY_RECIPE_MESSAGE = "[x] The recipe is currently empty. There is no module to be " + + "removed."; public RecipeException(String message) { super(message); From cd3a12b36635fca31053c2e35c7b492b8d714526 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 19 Oct 2021 00:05:45 +0800 Subject: [PATCH 218/607] Remove space handler for binary encoder and decoder --- .../decodex/modules/binary/BinaryDecoder.java | 19 ++++++++------- .../decodex/modules/binary/BinaryEncoder.java | 13 +++++----- .../modules/binary/BinaryDecoderTest.java | 24 +++++++------------ .../modules/binary/BinaryEncoderTest.java | 2 +- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java index cbd23a9033..790c692e9d 100644 --- a/src/main/java/decodex/modules/binary/BinaryDecoder.java +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -1,19 +1,22 @@ package decodex.modules.binary; -import decodex.data.Data; -import decodex.data.exception.ModuleException; import decodex.modules.Module; import java.util.regex.Matcher; import java.util.regex.Pattern; +import decodex.data.Data; +import decodex.data.exception.ModuleException; /** - * BinaryDecoder handles the binary decoding operations. + * The BinaryDecoder class handles the binary decoding operations. */ public class BinaryDecoder extends Module { public static final String MODULE_NAME = "bindecode"; public static final String MODULE_DESCRIPTION = "Decodes the data from binary."; + private static final int BINARY_RADIX = 2; + private static final String VALID_BINARY_REGEX = "^([01]{8})*$"; + public BinaryDecoder() { super(MODULE_NAME, MODULE_DESCRIPTION); } @@ -23,21 +26,21 @@ public BinaryDecoder() { * * @param data The Data to be decoded. * @return A Data object containing the binary decoded data. + * @throws ModuleException The Data is not in binary format. */ @Override public Data run(Data data) throws ModuleException { - String binString = data.toString().trim(); + String binString = data.toString(); if (!isValidBinary(binString)) { throw new ModuleException("Invalid binary string"); } - binString = binString.replace(" ", ""); String[] splitBinaryString = splitBinaryString(binString); byte[] newBytes = new byte[splitBinaryString.length]; for (int i = 0; i < splitBinaryString.length; i++) { - newBytes[i] = Byte.parseByte(splitBinaryString[i], 2); + newBytes[i] = Byte.parseByte(splitBinaryString[i], BINARY_RADIX); } return new Data(newBytes); @@ -50,7 +53,7 @@ public Data run(Data data) throws ModuleException { * @return True if provided binary string is valid. */ private boolean isValidBinary(String binString) { - Pattern validBinaryPattern = Pattern.compile("^([01]{8}[ ]*)*$"); + Pattern validBinaryPattern = Pattern.compile(VALID_BINARY_REGEX); Matcher validBinaryMatcher = validBinaryPattern.matcher(binString); return validBinaryMatcher.find(); } @@ -62,7 +65,7 @@ private boolean isValidBinary(String binString) { * @return A String array of 8 character binary strings. */ private String[] splitBinaryString(String binString) { - assert isValidBinary(binString) : "Binary string should be valid"; + assert binString.length() % 8 == 0 : "Binary string should be valid"; String[] splitString = new String[binString.length() / 8]; diff --git a/src/main/java/decodex/modules/binary/BinaryEncoder.java b/src/main/java/decodex/modules/binary/BinaryEncoder.java index 3ca4fa30a1..61b6287a6c 100644 --- a/src/main/java/decodex/modules/binary/BinaryEncoder.java +++ b/src/main/java/decodex/modules/binary/BinaryEncoder.java @@ -4,13 +4,15 @@ import decodex.modules.Module; /** - * BinaryEncoder handles the binary encoding operations. + * The BinaryEncoder class handles the binary encoding operations. */ public class BinaryEncoder extends Module { public static final String MODULE_NAME = "binencode"; public static final String MODULE_DESCRIPTION = "Encodes the data into binary."; + private static final int BYTE_LENGTH = 8; + public BinaryEncoder() { super(MODULE_NAME, MODULE_DESCRIPTION); } @@ -28,7 +30,6 @@ public Data run(Data data) { for (byte b : data.getRawBytes()) { String binString = leftPad(Integer.toBinaryString(b)); encodedStringBuilder.append(binString); - encodedStringBuilder.append(" "); } String encodedString = encodedStringBuilder.toString().trim(); @@ -42,10 +43,10 @@ public Data run(Data data) { * @return A padded binary string. */ private String leftPad(String binString) { - if (binString.length() < 8) { - return "0".repeat(8 - binString.length()) + binString; - } else { - return binString; + if (binString.length() < BYTE_LENGTH) { + return "0".repeat(BYTE_LENGTH - binString.length()) + binString; } + + return binString; } } diff --git a/src/test/java/decodex/modules/binary/BinaryDecoderTest.java b/src/test/java/decodex/modules/binary/BinaryDecoderTest.java index 3e3fc78111..af3f22c1f1 100644 --- a/src/test/java/decodex/modules/binary/BinaryDecoderTest.java +++ b/src/test/java/decodex/modules/binary/BinaryDecoderTest.java @@ -12,26 +12,12 @@ public class BinaryDecoderTest { BinaryDecoder binaryDecoder = new BinaryDecoder(); @Test - public void run_binaryStringWithSpaces_asciiString() throws ModuleException { - Data data = new Data(" 01110100 01100101 01110011 01110100 "); - Data newData = binaryDecoder.run(data); - assertEquals(newData.toString(), "test"); - } - - @Test - public void run_binaryStringWithoutSpaces_asciiString() throws ModuleException { + public void run_validBinaryString_asciiString() throws ModuleException { Data data = new Data("01110100011001010111001101110100"); Data newData = binaryDecoder.run(data); assertEquals(newData.toString(), "test"); } - @Test - public void run_binaryStringMixSpaces_asciiString() throws ModuleException { - Data data = new Data(" 01110100 0110010101110011 01110100"); - Data newData = binaryDecoder.run(data); - assertEquals(newData.toString(), "test"); - } - @Test public void run_blankString_blankBinaryString() throws ModuleException { Data data = new Data(""); @@ -46,9 +32,15 @@ public void run_nullByteBinaryString_success() throws ModuleException { assertEquals(newData.toString(), "\00"); } + @Test + public void run_binaryStringWithSpaces_moduleExceptionThrown() { + Data data = new Data("01110100 01100101 01110011 01110100"); + assertThrows(ModuleException.class, () -> binaryDecoder.run(data)); + } + @Test public void run_invalidBinaryString_moduleExceptionThrown() { - Data data = new Data("01110100 1110100"); + Data data = new Data("1110100"); assertThrows(ModuleException.class, () -> binaryDecoder.run(data)); } } diff --git a/src/test/java/decodex/modules/binary/BinaryEncoderTest.java b/src/test/java/decodex/modules/binary/BinaryEncoderTest.java index 9c79317083..c39a4a5371 100644 --- a/src/test/java/decodex/modules/binary/BinaryEncoderTest.java +++ b/src/test/java/decodex/modules/binary/BinaryEncoderTest.java @@ -13,7 +13,7 @@ public class BinaryEncoderTest { public void run_asciiString_binaryString() { Data data = new Data("test"); Data newData = binaryEncoder.run(data); - assertEquals(newData.toString(), "01110100 01100101 01110011 01110100"); + assertEquals(newData.toString(), "01110100011001010111001101110100"); } @Test From bdac3065b9afeca1595bfc097a70e4f03b7055a8 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 19 Oct 2021 00:43:21 +0800 Subject: [PATCH 219/607] Update binary decoder * Fix JavaDocs for `ModuleException` * Rename `splitBinaryString` variable to `binaryTokens` * Created `BYTE_LENGTH` as constant --- .../decodex/modules/binary/BinaryDecoder.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java index 790c692e9d..de8b726433 100644 --- a/src/main/java/decodex/modules/binary/BinaryDecoder.java +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -16,6 +16,7 @@ public class BinaryDecoder extends Module { private static final int BINARY_RADIX = 2; private static final String VALID_BINARY_REGEX = "^([01]{8})*$"; + private static final int BYTE_LENGTH = 8; public BinaryDecoder() { super(MODULE_NAME, MODULE_DESCRIPTION); @@ -26,7 +27,7 @@ public BinaryDecoder() { * * @param data The Data to be decoded. * @return A Data object containing the binary decoded data. - * @throws ModuleException The Data is not in binary format. + * @throws ModuleException If the Data is not in binary format. */ @Override public Data run(Data data) throws ModuleException { @@ -36,11 +37,11 @@ public Data run(Data data) throws ModuleException { throw new ModuleException("Invalid binary string"); } - String[] splitBinaryString = splitBinaryString(binString); + String[] binaryTokens = splitBinaryString(binString); - byte[] newBytes = new byte[splitBinaryString.length]; - for (int i = 0; i < splitBinaryString.length; i++) { - newBytes[i] = Byte.parseByte(splitBinaryString[i], BINARY_RADIX); + byte[] newBytes = new byte[binaryTokens.length]; + for (int i = 0; i < binaryTokens.length; i++) { + newBytes[i] = Byte.parseByte(binaryTokens[i], BINARY_RADIX); } return new Data(newBytes); @@ -65,12 +66,12 @@ private boolean isValidBinary(String binString) { * @return A String array of 8 character binary strings. */ private String[] splitBinaryString(String binString) { - assert binString.length() % 8 == 0 : "Binary string should be valid"; + assert binString.length() % BYTE_LENGTH == 0 : "Binary string should be valid"; - String[] splitString = new String[binString.length() / 8]; + String[] splitString = new String[binString.length() / BYTE_LENGTH]; - for (int i = 0; i < binString.length(); i += 8) { - splitString[i / 8] = binString.substring(i, i + 8); + for (int i = 0; i < binString.length(); i += BYTE_LENGTH) { + splitString[i / BYTE_LENGTH] = binString.substring(i, i + BYTE_LENGTH); } return splitString; From 3798b136863876e999c2f50dca645729e21e417c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 19 Oct 2021 17:23:08 +0800 Subject: [PATCH 220/607] Updated the file storage exceptions. --- src/main/java/decodex/Decodex.java | 2 +- .../decodex/data/exception/FileException.java | 2 ++ src/main/java/decodex/storage/Storage.java | 16 ++++++------ .../java/decodex/storage/StorageTest.java | 25 ------------------- 4 files changed, 11 insertions(+), 34 deletions(-) delete mode 100644 src/test/java/decodex/storage/StorageTest.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 8c000e4326..5fce3d2514 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -38,7 +38,7 @@ public Decodex() { /** * Initializes the necessary Objects for Decodex. */ - public static void initDecodex() { + private static void initDecodex() { logger.setLevel(Level.INFO); dataManager = new DataManager(); moduleManager = new ModuleManager(); diff --git a/src/main/java/decodex/data/exception/FileException.java b/src/main/java/decodex/data/exception/FileException.java index 99589b9ce2..05afc363bb 100644 --- a/src/main/java/decodex/data/exception/FileException.java +++ b/src/main/java/decodex/data/exception/FileException.java @@ -4,6 +4,8 @@ public class FileException extends Exception { public static final String DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE = "[x] The default input file does not exists"; + public static final String DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE = + "[x] The default input file failed to be created"; public static final String FILE_CREATION_ERROR_MESSAGE = "[x] Something went wrong while reading from default input file"; diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 7187e000fd..a6f77edaf7 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -26,7 +26,7 @@ public class Storage { /** * Initializes a new Storage. */ - public Storage() throws FileException { + public Storage() throws IOException { instantiateDirectories(); instantiateInputFile(); } @@ -37,7 +37,7 @@ public Storage() throws FileException { * @return The contents from the default input file. * @throws FileException If the default input file does not exist. */ - public String readFromDefaultInputFile() throws FileException { + public String readFromDefaultInputFile() throws FileNotFoundException { String inputContent = ""; File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); File inputFile = new File(inputDirectory, DEFAULT_INPUT_FILE); @@ -49,7 +49,7 @@ public String readFromDefaultInputFile() throws FileException { inputContent = inputContent + fileLine; } } catch (FileNotFoundException e) { - throw new FileException(FileException.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); + throw new FileNotFoundException(FileException.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); } return inputContent; } @@ -61,7 +61,7 @@ public String readFromDefaultInputFile() throws FileException { * @throws FileException If an I/O exception is caught when creating the output file * or when writing to the output file. */ - public void writeOutputToFile(String output) throws FileException { + public void writeOutputToFile(String output) throws IOException { LocalDateTime currentDateTime = LocalDateTime.now(); DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-dd-MM__HH.mm.ss"); String formattedDateTime = currentDateTime.format(dateTimeFormatter); @@ -73,7 +73,7 @@ public void writeOutputToFile(String output) throws FileException { try { outputFile.createNewFile(); } catch (IOException e) { - throw new FileException(FileException.FILE_CREATION_ERROR_MESSAGE); + throw new IOException(FileException.FILE_CREATION_ERROR_MESSAGE); } try { @@ -81,7 +81,7 @@ public void writeOutputToFile(String output) throws FileException { fw.write(output + "\n"); fw.close(); } catch (IOException e) { - e.printStackTrace(); + throw new IOException(FileException.FILE_WRITE_ERROR_MESSAGE); } } @@ -118,7 +118,7 @@ private void instantiateOutputDirectory() { * * @throws FileException If an I/O exception is caught when creating the file. */ - private void instantiateInputFile() throws FileException { + private void instantiateInputFile() throws IOException { File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); File inputFile = new File(inputDirectory, DEFAULT_INPUT_FILE); @@ -129,7 +129,7 @@ private void instantiateInputFile() throws FileException { try { inputFile.createNewFile(); } catch (IOException e) { - throw new FileException(FileException.FILE_CREATION_ERROR_MESSAGE); + throw new IOException(FileException.DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE); } } } diff --git a/src/test/java/decodex/storage/StorageTest.java b/src/test/java/decodex/storage/StorageTest.java deleted file mode 100644 index c7b52c8556..0000000000 --- a/src/test/java/decodex/storage/StorageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package decodex.storage; - -import decodex.data.exception.FileException; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class StorageTest { - - @Test - void readFromDefaultInputFile_inputFileWithSomeContent_stringContent() throws FileException { - Storage storage = new Storage(); - String inputContent = storage.readFromDefaultInputFile(); - - assertEquals("TESTING FOR JUNIT", inputContent); - } - - @Test - void writeOutputToFile_decodedOrEncodedString_successfulWriteToFile() throws FileException { - Storage storage = new Storage(); - String testStringToBeWritten = "Hi I am a Test!"; - - storage.writeOutputToFile(testStringToBeWritten); - } -} From 9ae15c16af60387fe83d02a4c3824fb9910dbde7 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 19 Oct 2021 17:24:49 +0800 Subject: [PATCH 221/607] Added requested changes --- src/main/java/decodex/recipes/Recipe.java | 13 ++++++++++--- src/test/java/decodex/recipes/RecipeTest.java | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index ff100b047f..0280e45d0d 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -15,9 +15,15 @@ public class Recipe { private Logger logger = Decodex.logger; private String name; - private ArrayList moduleList = new ArrayList<>(); + private ArrayList moduleList; + /** + * Instantiates a new recipe of modules. + * + * @param name The name of the recipe object to be created. + */ public Recipe(String name) { + moduleList = new ArrayList<>(); this.name = name; } @@ -26,6 +32,7 @@ public String getName() { } public void setName(String name) { + assert name != null; this.name = name; } @@ -36,7 +43,7 @@ public ArrayList getModuleList() { /** * Appends a new module to the end of the module list. * - * @param module Module to be added to the recipe. + * @param module The module to be added to the recipe. */ public void push(Module module) { moduleList.add(module); @@ -46,7 +53,7 @@ public void push(Module module) { /** * Removes the latest module that was added to the list. * - * @return Module that was popped from the list + * @return The module that was popped from the list. */ public Module pop() throws RecipeException { if (moduleList.isEmpty()) { diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index 691c5efb08..8f93eba45a 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -31,7 +31,7 @@ void pop_singleModule_latestModule() throws RecipeException { Module poppedModule = recipe.pop(); - assertEquals(poppedModule, poppedModule); + assertEquals(module, poppedModule); } @Test From 5fd308c04ae55aee8660e6f3d2e91945aa5e0818 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 19 Oct 2021 17:29:56 +0800 Subject: [PATCH 222/607] Rename variable `splitString` to `binaryTokens` --- src/main/java/decodex/modules/binary/BinaryDecoder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java index de8b726433..bb0d742e63 100644 --- a/src/main/java/decodex/modules/binary/BinaryDecoder.java +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -68,12 +68,12 @@ private boolean isValidBinary(String binString) { private String[] splitBinaryString(String binString) { assert binString.length() % BYTE_LENGTH == 0 : "Binary string should be valid"; - String[] splitString = new String[binString.length() / BYTE_LENGTH]; + String[] binaryTokens = new String[binString.length() / BYTE_LENGTH]; for (int i = 0; i < binString.length(); i += BYTE_LENGTH) { - splitString[i / BYTE_LENGTH] = binString.substring(i, i + BYTE_LENGTH); + binaryTokens[i / BYTE_LENGTH] = binString.substring(i, i + BYTE_LENGTH); } - return splitString; + return binaryTokens; } } From 4a77a6181faa4bf6184f9cd4c903d26b3afd9690 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 19 Oct 2021 17:35:22 +0800 Subject: [PATCH 223/607] Added requested changes --- .../exception/RecipeManagerException.java | 4 +- .../java/decodex/recipes/RecipeManager.java | 45 ++++++++----------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/main/java/decodex/data/exception/RecipeManagerException.java b/src/main/java/decodex/data/exception/RecipeManagerException.java index 72471223f8..0b38f4b10c 100644 --- a/src/main/java/decodex/data/exception/RecipeManagerException.java +++ b/src/main/java/decodex/data/exception/RecipeManagerException.java @@ -5,8 +5,8 @@ */ public class RecipeManagerException extends Exception { - public static final String EMPTY_RECIPELIST_MESSAGE = "[x] There is nothing to be removed."; - public static final String RECIPIE_NOT_FOUND_MESSAGE = "[x] Given recipe could not be found."; + public static final String DUPLICATE_RECIPE_NAME_MESSAGE = "[x] A recipe with the given name already exists."; + public static final String RECIPE_NOT_FOUND_MESSAGE = "[x] The given recipe could not be found."; public RecipeManagerException(String message) { super(message); diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 9dd9ce4053..31b3672cb5 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -2,7 +2,7 @@ import decodex.Decodex; import decodex.data.exception.RecipeManagerException; -import java.util.ArrayList; +import java.util.HashMap; import java.util.logging.Logger; /** @@ -12,40 +12,35 @@ public class RecipeManager { private Logger logger = Decodex.logger; - ArrayList recipeList; + HashMap recipeList; /** * Creates a new RecipeManager with no recipes. */ public RecipeManager() { - this.recipeList = new ArrayList<>(); - } - - /** - * Creates a new RecipeManager with a predefined list of recipes. - * - * @param recipeList list of recipes to be loaded. - */ - public RecipeManager(ArrayList recipeList) { - this.recipeList = recipeList; + recipeList = new HashMap<>(); } /** * Adds a recipe to recipe manager to be managed. * - * @param recipe Recipe to be added to the recipe manager. + * @param recipe The recipe to be added to the recipe manager. + * @throws RecipeManagerException If the given recipe name already exists in recipe manager. */ - public void addRecipe(Recipe recipe) { - recipeList.add(recipe); + public void addRecipe(Recipe recipe) throws RecipeManagerException { + if (recipeList.containsKey(recipe.getName())) { + throw new RecipeManagerException(RecipeManagerException.DUPLICATE_RECIPE_NAME_MESSAGE); + } + recipeList.put(recipe.getName(), recipe); logger.fine(String.format("[RecipeManager] Added recipe %s", recipe.getName())); } /** * Removes a recipe from the recipe manager. * - * @param name Name of the recipe to be removed. - * @return Recipe that was removed. - * @throws RecipeManagerException Recipe could not be found in the recipe manager. + * @param name The name of the recipe to be removed. + * @return The recipe that was removed. + * @throws RecipeManagerException If the recipe could not be found in the recipe manager. */ public Recipe removeRecipe(String name) throws RecipeManagerException { Recipe recipeToRemove = getRecipe(name); @@ -57,17 +52,15 @@ public Recipe removeRecipe(String name) throws RecipeManagerException { /** * Get a Recipe object from the recipe manager by name. * - * @param name Name of the recipe to get. - * @return Recipe that was found. - * @throws RecipeManagerException Recipe could not be found in the recipe manager. + * @param name The name of the recipe to get. + * @return The recipe that was found. + * @throws RecipeManagerException If the recipe could not be found in the recipe manager. */ public Recipe getRecipe(String name) throws RecipeManagerException { - for (Recipe recipe : recipeList) { - if (recipe.getName().equals(name)) { - return recipe; - } + if (!recipeList.containsKey(name)) { + throw new RecipeManagerException(RecipeManagerException.RECIPE_NOT_FOUND_MESSAGE); } - throw new RecipeManagerException(RecipeManagerException.RECIPIE_NOT_FOUND_MESSAGE); + return recipeList.get(name); } } From ca615e4cd0e2d629c9641ae73002b07b3d4066f8 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 19 Oct 2021 17:37:12 +0800 Subject: [PATCH 224/607] Added a Javadoc for the exception --- src/main/java/decodex/recipes/Recipe.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index 0280e45d0d..1ceb7fdb8a 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -54,6 +54,7 @@ public void push(Module module) { * Removes the latest module that was added to the list. * * @return The module that was popped from the list. + * @throws RecipeException If there are no modules in the recipe. */ public Module pop() throws RecipeException { if (moduleList.isEmpty()) { From 9833bc65417a18ee861966780a991bb1db72158d Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 19 Oct 2021 17:41:48 +0800 Subject: [PATCH 225/607] Add message to assert --- src/main/java/decodex/recipes/Recipe.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index 1ceb7fdb8a..a3d005c79c 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -32,7 +32,7 @@ public String getName() { } public void setName(String name) { - assert name != null; + assert name != null : "Recipe name should not be null"; this.name = name; } From 6d3209423ba20d66439aa21c0a388d0b09288eee Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 19 Oct 2021 17:42:08 +0800 Subject: [PATCH 226/607] Updated error messages to be in ErrorMessages.java --- .../decodex/data/exception/FileException.java | 17 ----------------- src/main/java/decodex/storage/Storage.java | 16 ++++++++-------- .../java/decodex/ui/messages/ErrorMessages.java | 7 +++++++ 3 files changed, 15 insertions(+), 25 deletions(-) delete mode 100644 src/main/java/decodex/data/exception/FileException.java diff --git a/src/main/java/decodex/data/exception/FileException.java b/src/main/java/decodex/data/exception/FileException.java deleted file mode 100644 index 05afc363bb..0000000000 --- a/src/main/java/decodex/data/exception/FileException.java +++ /dev/null @@ -1,17 +0,0 @@ -package decodex.data.exception; - -public class FileException extends Exception { - - public static final String DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE = - "[x] The default input file does not exists"; - public static final String DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE = - "[x] The default input file failed to be created"; - public static final String FILE_CREATION_ERROR_MESSAGE = - "[x] Something went wrong while reading from default input file"; - - public static final String FILE_WRITE_ERROR_MESSAGE = "[x] Something went wrong while writing to output file"; - - public FileException(String message) { - super(message); - } -} diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index a6f77edaf7..47462f079e 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -1,6 +1,6 @@ package decodex.storage; -import decodex.data.exception.FileException; +import decodex.ui.messages.ErrorMessages; import java.io.File; import java.io.FileNotFoundException; @@ -35,7 +35,7 @@ public Storage() throws IOException { * Reads the contents from the default input file. * * @return The contents from the default input file. - * @throws FileException If the default input file does not exist. + * @throws IOException If the default input file does not exist. */ public String readFromDefaultInputFile() throws FileNotFoundException { String inputContent = ""; @@ -49,7 +49,7 @@ public String readFromDefaultInputFile() throws FileNotFoundException { inputContent = inputContent + fileLine; } } catch (FileNotFoundException e) { - throw new FileNotFoundException(FileException.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); + throw new FileNotFoundException(ErrorMessages.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); } return inputContent; } @@ -58,7 +58,7 @@ public String readFromDefaultInputFile() throws FileNotFoundException { * Writes the given output into a file. * * @param output The encoded or decoded output. - * @throws FileException If an I/O exception is caught when creating the output file + * @throws IOException If an I/O exception is caught when creating the output file * or when writing to the output file. */ public void writeOutputToFile(String output) throws IOException { @@ -73,7 +73,7 @@ public void writeOutputToFile(String output) throws IOException { try { outputFile.createNewFile(); } catch (IOException e) { - throw new IOException(FileException.FILE_CREATION_ERROR_MESSAGE); + throw new IOException(ErrorMessages.FILE_CREATION_ERROR_MESSAGE); } try { @@ -81,7 +81,7 @@ public void writeOutputToFile(String output) throws IOException { fw.write(output + "\n"); fw.close(); } catch (IOException e) { - throw new IOException(FileException.FILE_WRITE_ERROR_MESSAGE); + throw new IOException(ErrorMessages.FILE_WRITE_ERROR_MESSAGE); } } @@ -116,7 +116,7 @@ private void instantiateOutputDirectory() { /** * Instantiates the default input file. * - * @throws FileException If an I/O exception is caught when creating the file. + * @throws IOException If an I/O exception is caught when creating the file. */ private void instantiateInputFile() throws IOException { File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); @@ -129,7 +129,7 @@ private void instantiateInputFile() throws IOException { try { inputFile.createNewFile(); } catch (IOException e) { - throw new IOException(FileException.DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE); + throw new IOException(ErrorMessages.DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE); } } } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index e5bf5d4640..65f2f0e080 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -10,4 +10,11 @@ public class ErrorMessages { public static final String NO_DATA_FOUND = "No data found"; public static final String BASE64_DECODING_FAILED_MESSAGE = "Your data does not seem to be in Base64 format"; public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; + public static final String DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE = + "The default input file does not exists"; + public static final String DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE = + "The default input file failed to be created"; + public static final String FILE_CREATION_ERROR_MESSAGE = + "Something went wrong while reading from default input file"; + public static final String FILE_WRITE_ERROR_MESSAGE = "Something went wrong while writing to output file"; } From d0a1dc4e9ca8bce7b5b9dd717f71bac8c24d29c1 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 19 Oct 2021 18:05:25 +0800 Subject: [PATCH 227/607] Updated to remove instantiating of default input file. Additional - Change "Craft" to "Prepare" --- src/main/java/decodex/parser/Parser.java | 20 ++++++++++---------- src/main/java/decodex/storage/Storage.java | 21 --------------------- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 7bd28e8d6f..7d166672a0 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -109,19 +109,19 @@ public Command parseCommand(String userInput) throws ParserException { switch (commandType) { case ExitCommand.COMMAND_WORD: - command = craftExitCommand(); + command = prepareExitCommand(); break; case InputCommand.COMMAND_WORD: - command = craftInputCommand(userInput); + command = prepareInputCommand(userInput); break; case ListCommand.COMMAND_WORD: - command = craftListCommand(); + command = prepareListCommand(); break; case ResetCommand.COMMAND_WORD: - command = craftResetCommand(); + command = prepareResetCommand(); break; case SelectCommand.COMMAND_WORD: - command = craftSelectCommand(userInput); + command = prepareSelectCommand(userInput); break; default: throw new ParserException(ErrorMessages.UNKNOWN_COMMAND); @@ -134,7 +134,7 @@ public Command parseCommand(String userInput) throws ParserException { * * @return The ExitCommand object. */ - private ExitCommand craftExitCommand() { + private ExitCommand prepareExitCommand() { return new ExitCommand(); } @@ -145,7 +145,7 @@ private ExitCommand craftExitCommand() { * @return The InputCommand object. * @throws ParserException If the argument string is empty. */ - private InputCommand craftInputCommand(String userInput) throws ParserException { + private InputCommand prepareInputCommand(String userInput) throws ParserException { String inputData = getInputString(userInput); return new InputCommand(inputData); } @@ -155,7 +155,7 @@ private InputCommand craftInputCommand(String userInput) throws ParserException * * @return The ListCommand object. */ - private ListCommand craftListCommand() { + private ListCommand prepareListCommand() { return new ListCommand(); } @@ -164,7 +164,7 @@ private ListCommand craftListCommand() { * * @return The ResetCommand object. */ - private ResetCommand craftResetCommand() { + private ResetCommand prepareResetCommand() { return new ResetCommand(); } @@ -175,7 +175,7 @@ private ResetCommand craftResetCommand() { * @return The SelectCommand object. * @throws ParserException If the user input is missing the module name. */ - private SelectCommand craftSelectCommand(String userInput) throws ParserException { + private SelectCommand prepareSelectCommand(String userInput) throws ParserException { String moduleName = getModuleName(userInput); return new SelectCommand(moduleName); } diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 47462f079e..6bf0a80a68 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -28,7 +28,6 @@ public class Storage { */ public Storage() throws IOException { instantiateDirectories(); - instantiateInputFile(); } /** @@ -112,24 +111,4 @@ private void instantiateOutputDirectory() { outputDirectory.mkdir(); } } - - /** - * Instantiates the default input file. - * - * @throws IOException If an I/O exception is caught when creating the file. - */ - private void instantiateInputFile() throws IOException { - File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); - File inputFile = new File(inputDirectory, DEFAULT_INPUT_FILE); - - if (inputFile.exists()) { - return; - } - - try { - inputFile.createNewFile(); - } catch (IOException e) { - throw new IOException(ErrorMessages.DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE); - } - } } From fc995c4a005ed54014f817815611830af58aadb3 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 19 Oct 2021 18:07:07 +0800 Subject: [PATCH 228/607] Added suggested changes --- src/main/java/decodex/Decodex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 2b3214f539..a890ddac13 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -35,7 +35,7 @@ public Decodex() { /** * Initializes the necessary Objects for Decodex. */ - private static void initDecodex() { + private void initDecodex() { logger.setLevel(Level.INFO); dataManager = new DataManager(); moduleManager = new ModuleManager(); From 83af82a09b56d34abefca3631de5ed7ce5e61a4b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 19 Oct 2021 18:12:52 +0800 Subject: [PATCH 229/607] Added suggested change to take in input from user instead --- src/main/java/decodex/storage/Storage.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 6bf0a80a68..7cbff0e560 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -16,7 +16,6 @@ public class Storage { * Variables for loading from the default input filepath. */ public static final String DEFAULT_INPUT_DIRECTORY = "input"; - public static final String DEFAULT_INPUT_FILE = "input.txt"; /** * Variable for the default output directory. @@ -33,13 +32,14 @@ public Storage() throws IOException { /** * Reads the contents from the default input file. * + * @param fileName The name of the file specified by the user. * @return The contents from the default input file. * @throws IOException If the default input file does not exist. */ - public String readFromDefaultInputFile() throws FileNotFoundException { + public String readFromDefaultInputFile(String fileName) throws FileNotFoundException { String inputContent = ""; File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); - File inputFile = new File(inputDirectory, DEFAULT_INPUT_FILE); + File inputFile = new File(inputDirectory, fileName); try { Scanner in = new Scanner(inputFile); From 76e9f5bb23e69ef916e98bc263ca5d1c859597de Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 19 Oct 2021 18:47:43 +0800 Subject: [PATCH 230/607] Changed reading of file contents to use paths instead. --- src/main/java/decodex/storage/Storage.java | 23 ++++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 7cbff0e560..6b0a6400bf 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -3,12 +3,12 @@ import decodex.ui.messages.ErrorMessages; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Scanner; public class Storage { @@ -33,22 +33,19 @@ public Storage() throws IOException { * Reads the contents from the default input file. * * @param fileName The name of the file specified by the user. - * @return The contents from the default input file. + * @return The byte contents from the default input file. * @throws IOException If the default input file does not exist. */ - public String readFromDefaultInputFile(String fileName) throws FileNotFoundException { - String inputContent = ""; + public byte[] readFromDefaultInputFile(String fileName) throws IOException { + byte[] inputContent; File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); File inputFile = new File(inputDirectory, fileName); + Path inputFilePath = inputFile.toPath(); try { - Scanner in = new Scanner(inputFile); - while (in.hasNext()) { - String fileLine = in.nextLine(); - inputContent = inputContent + fileLine; - } - } catch (FileNotFoundException e) { - throw new FileNotFoundException(ErrorMessages.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); + inputContent = Files.readAllBytes(inputFilePath); + } catch (IOException e) { + throw new IOException(ErrorMessages.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); } return inputContent; } @@ -58,7 +55,7 @@ public String readFromDefaultInputFile(String fileName) throws FileNotFoundExcep * * @param output The encoded or decoded output. * @throws IOException If an I/O exception is caught when creating the output file - * or when writing to the output file. + * or when writing to the output file. */ public void writeOutputToFile(String output) throws IOException { LocalDateTime currentDateTime = LocalDateTime.now(); From e25958101a4175812e2a47e896387c6333b83225 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 19 Oct 2021 19:55:34 +0800 Subject: [PATCH 231/607] Modify `ModuleManager` to accept module parameters * Update `Parser` to handle parameters * Update modules that rely on `ModuleManager.selectModule()` * Update tests --- .../java/decodex/commands/ListCommand.java | 15 ++- .../java/decodex/commands/SelectCommand.java | 6 +- .../java/decodex/modules/ModuleManager.java | 123 +++++++++++++++--- src/main/java/decodex/parser/Parser.java | 32 ++++- .../decodex/ui/messages/ErrorMessages.java | 3 + .../decodex/commands/SelectCommandTest.java | 27 +++- .../decodex/modules/ModuleManagerTest.java | 51 +++++++- 7 files changed, 223 insertions(+), 34 deletions(-) diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 707ef8c0f4..149cf848f4 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -1,13 +1,14 @@ package decodex.commands; import decodex.data.DataManager; -import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.ui.Ui; public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; + private static final int MODULE_NAME_INDEX = 0; + private static final int MODULE_DESCRIPTION_INDEX = 1; public ListCommand() { super(); @@ -15,24 +16,24 @@ public ListCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { - Module[] modules = moduleManager.getModules(); + String[][] modules = moduleManager.getModules(); assert modules.length > 0 : "Number of modules should be greater than 0"; StringBuilder moduleListString = new StringBuilder(); int maxNameWidth = 0; // Find number of characters of the longest module name - for (Module module : modules) { - String moduleName = module.getName(); + for (String[] module : modules) { + String moduleName = module[MODULE_NAME_INDEX]; if (moduleName.length() > maxNameWidth) { maxNameWidth = moduleName.length(); } } // Prepare and format list of modules - for (Module module : modules) { - String moduleName = module.getName(); - String moduleDescription = module.getDescription(); + for (String[] module : modules) { + String moduleName = module[MODULE_NAME_INDEX]; + String moduleDescription = module[MODULE_DESCRIPTION_INDEX]; moduleListString.append(String.format(" %-" + maxNameWidth + "s - %s\n", moduleName, moduleDescription)); } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index 6383e65c67..3002b589af 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -16,10 +16,12 @@ public class SelectCommand extends Command { public static final String COMMAND_WORD = "select"; private final String moduleName; + private final String[] parameters; - public SelectCommand(String moduleName) { + public SelectCommand(String moduleName, String[] parameters) { super(); this.moduleName = moduleName; + this.parameters = parameters; } @Override @@ -29,7 +31,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); } - Module module = moduleManager.selectModule(moduleName); + Module module = moduleManager.selectModule(moduleName, parameters); Data newData = module.run(dataManager.getCurrentData()); dataManager.setCurrentData(newData); diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 10b24c2b45..16cad359c2 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -1,51 +1,142 @@ package decodex.modules; +import java.util.logging.Logger; + import decodex.Decodex; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; +import decodex.modules.binary.BinaryDecoder; +import decodex.modules.binary.BinaryEncoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; -import java.util.logging.Logger; +import decodex.modules.rot.RotEncoder; +import decodex.ui.messages.ErrorMessages; /** * ModuleManager maintains the list of available modules and returns selected modules. */ public class ModuleManager { - private Logger logger = Decodex.logger; + private final Logger logger = Decodex.logger; + + private static final int MODULE_NAME_INDEX = 0; /** - * Array of available modules. + * Available modules. */ - private final Module[] modules = {new Base64Encoder(), new Base64Decoder(), new HexEncoder(), new HexDecoder()}; + private static final String[][] MODULE_LIST = { + {Base64Encoder.MODULE_NAME, Base64Encoder.MODULE_DESCRIPTION}, + {HexEncoder.MODULE_NAME, HexDecoder.MODULE_DESCRIPTION}, + {BinaryEncoder.MODULE_NAME, BinaryDecoder.MODULE_DESCRIPTION}, + {RotEncoder.MODULE_NAME, RotEncoder.MODULE_DESCRIPTION} + }; public ModuleManager() { // Log available modules - for (Module module : modules) { - logger.fine(String.format("Loaded %s", module.getName())); + for (String[] module : MODULE_LIST) { + logger.fine(String.format("Loaded %s", module[MODULE_NAME_INDEX])); } } - public Module[] getModules() { - return modules; + public String[][] getModules() { + return MODULE_LIST; } /** * Select module from a provided module name. * * @param moduleName Name of selected module. + * @param parameters Parameters for specific commands. * @return Selected module. - * @throws UnknownModuleException Not a module name of an available module. + * @throws UnknownModuleException If provided module name is not an available module. + * @throws ModuleException If module parameters are invalid. */ - public Module selectModule(String moduleName) throws UnknownModuleException { - for (Module module : modules) { - if (module.getName().equals(moduleName)) { - logger.fine(String.format("Selected %s", module.getName())); - return module; - } + public Module selectModule(String moduleName, String[] parameters) throws UnknownModuleException, ModuleException { + Module module; + + switch (moduleName) { + case Base64Encoder.MODULE_NAME: + module = prepareBase64Encoder(); + break; + case Base64Decoder.MODULE_NAME: + module = prepareBase64Decoder(); + break; + case HexEncoder.MODULE_NAME: + module = prepareHexEncoder(); + break; + case HexDecoder.MODULE_NAME: + module = prepareHexDecoder(); + break; + case RotEncoder.MODULE_NAME: + module = prepareRotEncoder(parameters); + break; + default: + throw new UnknownModuleException(moduleName); } - throw new UnknownModuleException(moduleName); + logger.fine(String.format("Selected %s", module.getName())); + return module; + } + + /** + * Prepares and returns the Base64Encoder. + * + * @return The Base64Encoder object. + */ + private Module prepareBase64Encoder() { + return new Base64Encoder(); + } + + /** + * Prepares and returns the Base64Decoder. + * + * @return The Base64Decoder object. + */ + private Module prepareBase64Decoder() { + return new Base64Decoder(); + } + + /** + * Prepares and returns the HexEncoder. + * + * @return The HexEncoder object. + */ + private Module prepareHexEncoder() { + return new HexEncoder(); + } + + /** + * Prepares and returns the HexDecoder. + * + * @return The HexDecoder object. + */ + private Module prepareHexDecoder() { + return new HexDecoder(); + } + + /** + * Prepares and returns the RotEncoder with specified rotation. + * + * @param parameters String array of module parameters. + * @return The RotEncoder object. + * @throws ModuleException If the number of parameters are incorrect or invalid. + */ + private Module prepareRotEncoder(String[] parameters) throws ModuleException { + if (parameters == null) { + throw new ModuleException(ErrorMessages.MISSING_MODULE_PARAMETERS); + } + + if (parameters.length > 1) { + throw new ModuleException(ErrorMessages.TOO_MANY_MODULE_PARAMETERS); + } + + // First parameter is the rotation offset + try { + return new RotEncoder(Integer.parseInt(parameters[0])); + } catch (NumberFormatException e) { + throw new ModuleException(ErrorMessages.INVALID_MODULE_PARAMETERS); + } } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 7bd28e8d6f..2d11a6d987 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -1,5 +1,7 @@ package decodex.parser; +import java.util.Arrays; + import decodex.commands.Command; import decodex.commands.ExitCommand; import decodex.commands.InputCommand; @@ -8,7 +10,6 @@ import decodex.commands.SelectCommand; import decodex.data.exception.ParserException; import decodex.ui.messages.ErrorMessages; -import java.util.Arrays; /** * The Parser class handles the parsing and validation of the user input. @@ -26,6 +27,16 @@ public class Parser { */ private static final int STARTING_ARGUMENTS_INDEX = 1; + /** + * Specifies the starting index where the module parameters can be found. + */ + private static final int MODULE_PARAMETERS_INDEX = 2; + + /** + * Specifies the token used to split the user input by. + */ + private static final String SPLIT_REGEX = "\\s+"; + /** * Specifies the valid length of the tokens and used to check validity of tokens. */ @@ -77,6 +88,22 @@ public String getModuleName(String userInput) throws ParserException { return argumentTokens[MODULE_NAME_INDEX_IN_TOKENS]; } + /** + * Returns the module parameters that the user has specified. + * + * @param userInput The input specified by the user. + * @return A String array of module parameters if provided. Otherwise, returns null. + */ + public String[] getModuleParameters(String userInput) { + String strippedUserInput = userInput.stripLeading(); + String[] tokens = strippedUserInput.split(SPLIT_REGEX); + String[] parameters = Arrays.copyOfRange(tokens, MODULE_PARAMETERS_INDEX, tokens.length); + if (parameters.length > 0) { + return parameters; + } + return null; + } + /** * Returns the InputCommand's argument string that the user has specified. * @@ -177,7 +204,8 @@ private ResetCommand craftResetCommand() { */ private SelectCommand craftSelectCommand(String userInput) throws ParserException { String moduleName = getModuleName(userInput); - return new SelectCommand(moduleName); + String[] parameters = getModuleParameters(userInput); + return new SelectCommand(moduleName, parameters); } } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index e5bf5d4640..2a13f29ccb 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -10,4 +10,7 @@ public class ErrorMessages { public static final String NO_DATA_FOUND = "No data found"; public static final String BASE64_DECODING_FAILED_MESSAGE = "Your data does not seem to be in Base64 format"; public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; + public static final String INVALID_MODULE_PARAMETERS = "Invalid module parameters"; + public static final String MISSING_MODULE_PARAMETERS = "Missing module parameters"; + public static final String TOO_MANY_MODULE_PARAMETERS = "Too many module parameters"; } diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index abf98b4315..0effe0069f 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -27,12 +27,31 @@ public void run_validModuleName_success() dataManager.setOriginalData(data); String moduleName = "base64encode"; - SelectCommand selectCommand = new SelectCommand(moduleName); + String[] parameters = null; + SelectCommand selectCommand = new SelectCommand(moduleName, parameters); selectCommand.run(dataManager, moduleManager, ui); assertEquals("aGVsbG8gd29ybGQ=", dataManager.getCurrentData().toString()); } + @Test + public void run_moduleWithParameters_success() + throws UnknownModuleException, CommandException, ModuleException, DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + Data data = new Data("hello world"); + dataManager.setOriginalData(data); + + String moduleName = "rotencode"; + String[] parameters = {"13"}; + SelectCommand selectCommand = new SelectCommand(moduleName, parameters); + selectCommand.run(dataManager, moduleManager, ui); + + assertEquals("uryyb jbeyq", dataManager.getCurrentData().toString()); + } + @Test public void run_blankModuleName_expectException() throws CommandException { DataManager dataManager = new DataManager(); @@ -43,7 +62,8 @@ public void run_blankModuleName_expectException() throws CommandException { dataManager.setOriginalData(data); String moduleName = ""; - SelectCommand selectCommand = new SelectCommand(moduleName); + String[] parameters = null; + SelectCommand selectCommand = new SelectCommand(moduleName, parameters); assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); } @@ -58,7 +78,8 @@ public void run_unknownModuleName_expectException() throws CommandException { dataManager.setOriginalData(data); String moduleName = "unknownModule"; - SelectCommand selectCommand = new SelectCommand(moduleName); + String[] parameters = null; + SelectCommand selectCommand = new SelectCommand(moduleName, parameters); assertThrows(UnknownModuleException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); } diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index 915e8d540a..ba9e360bc2 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -3,32 +3,75 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import decodex.data.exception.ModuleException; import decodex.data.exception.UnknownModuleException; import decodex.modules.base64.Base64Encoder; +import decodex.modules.rot.RotEncoder; + import org.junit.jupiter.api.Test; public class ModuleManagerTest { @Test - public void selectModule_base64encode_success() throws UnknownModuleException { + public void selectModule_base64encode_success() throws UnknownModuleException, ModuleException { String moduleName = "base64encode"; + String[] parameters = null; ModuleManager moduleManager = new ModuleManager(); - Module module = moduleManager.selectModule(moduleName); + Module module = moduleManager.selectModule(moduleName, parameters); assertTrue(module instanceof Base64Encoder); } + @Test + public void selectModule_rotEncodeWithValidParameter_success() throws UnknownModuleException, ModuleException { + String moduleName = "rotencode"; + String[] parameters = {"13"}; + ModuleManager moduleManager = new ModuleManager(); + + Module module = moduleManager.selectModule(moduleName, parameters); + assertTrue(module instanceof RotEncoder); + } + @Test public void selectModule_unknownModuleName_unknownModuleExceptionThrown() { String moduleName = "unknownModule"; + String[] parameters = null; ModuleManager moduleManager = new ModuleManager(); - assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName)); + assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); } @Test public void selectModule_blankModuleName_unknownModuleExceptionThrown() { String moduleName = ""; + String[] parameters = null; ModuleManager moduleManager = new ModuleManager(); - assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName)); + assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); + } + + @Test + public void selectModule_rotEncodeWithoutParameters_success() { + String moduleName = "rotencode"; + String[] parameters = null; + ModuleManager moduleManager = new ModuleManager(); + + assertThrows(ModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); + } + + @Test + public void selectModule_rotEncodeAdditionalParameters_success() { + String moduleName = "rotencode"; + String[] parameters = {"13", "a"}; + ModuleManager moduleManager = new ModuleManager(); + + assertThrows(ModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); + } + + @Test + public void selectModule_rotEncodeInvalidParameters_success() { + String moduleName = "rotencode"; + String[] parameters = {"a"}; + ModuleManager moduleManager = new ModuleManager(); + + assertThrows(ModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); } } From 7d62ae743c088789bd01411b0360515b33528e40 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 20 Oct 2021 01:44:24 +0800 Subject: [PATCH 232/607] Add RecipeManager to commands --- src/main/java/decodex/Decodex.java | 10 ++++++--- src/main/java/decodex/commands/Command.java | 8 +++++-- .../java/decodex/commands/ExitCommand.java | 3 ++- .../java/decodex/commands/InputCommand.java | 4 +++- .../java/decodex/commands/ListCommand.java | 3 ++- .../java/decodex/commands/ResetCommand.java | 3 ++- .../java/decodex/commands/SelectCommand.java | 8 +++++-- .../decodex/commands/InputCommandTest.java | 15 ++++++------- .../decodex/commands/ResetCommandTest.java | 21 +++++++++---------- .../decodex/commands/SelectCommandTest.java | 21 ++++++++++++------- 10 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 472e765fe5..f91f17577e 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -7,11 +7,13 @@ import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; import decodex.data.exception.ParserException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.parser.Parser; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; -import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; @@ -25,6 +27,7 @@ public class Decodex { */ private static DataManager dataManager; private static ModuleManager moduleManager; + private static RecipeManager recipeManager; private static Parser parser; private static Ui ui; @@ -39,6 +42,7 @@ public static void initDecodex() { logger.setLevel(Level.INFO); dataManager = new DataManager(); moduleManager = new ModuleManager(); + recipeManager = new RecipeManager(); parser = new Parser(); ui = new Ui(); } @@ -61,9 +65,9 @@ public void run() { try { command = parser.parseCommand(userInput); assert command != null : "Command should not be null"; - command.run(dataManager, moduleManager, ui); + command.run(dataManager, moduleManager, ui, recipeManager); } catch (ParserException | CommandException | UnknownModuleException - | DataManagerException | ModuleException err) { + | DataManagerException | ModuleException | RecipeException | RecipeManagerException err) { ui.printError(err); logger.fine(err.getMessage()); } diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index f305b8f132..3af70455a0 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -4,8 +4,11 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; /** @@ -16,6 +19,7 @@ public abstract class Command { public Command() { } - public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) - throws CommandException, UnknownModuleException, DataManagerException, ModuleException; + public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, UnknownModuleException, DataManagerException, ModuleException, RecipeException, + RecipeManagerException; } diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java index ffd4fe435c..7258c015ed 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -2,6 +2,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; public class ExitCommand extends Command { @@ -13,7 +14,7 @@ public ExitCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { ui.printGoodbye(); } } diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index bd58f7adfb..05271357c0 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -4,6 +4,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -19,7 +20,8 @@ public InputCommand(String dataString) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws CommandException { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException { if (dataString.isEmpty()) { throw new CommandException(ErrorMessages.MISSING_ARGUMENT); } diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 707ef8c0f4..cda4ae59a5 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.modules.Module; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; public class ListCommand extends Command { @@ -14,7 +15,7 @@ public ListCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { Module[] modules = moduleManager.getModules(); assert modules.length > 0 : "Number of modules should be greater than 0"; diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java index 46ccaa1d0e..1dcdc7572b 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -2,6 +2,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.RegularMessages; @@ -14,7 +15,7 @@ public ResetCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { dataManager.resetToOriginalData(); ui.printSuccess(RegularMessages.REVERTED_ALL_CHANGES); } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index 6383e65c67..620e0eaa95 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -5,9 +5,12 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; import decodex.data.exception.UnknownModuleException; import decodex.modules.Module; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -23,8 +26,9 @@ public SelectCommand(String moduleName) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui) - throws UnknownModuleException, CommandException, DataManagerException, ModuleException { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws UnknownModuleException, CommandException, DataManagerException, ModuleException, RecipeException, + RecipeManagerException { if (moduleName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); } diff --git a/src/test/java/decodex/commands/InputCommandTest.java b/src/test/java/decodex/commands/InputCommandTest.java index 0f6e9cffbf..bc7747d237 100644 --- a/src/test/java/decodex/commands/InputCommandTest.java +++ b/src/test/java/decodex/commands/InputCommandTest.java @@ -3,8 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.data.exception.ModuleException; -import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -14,6 +12,7 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; public class InputCommandTest { @@ -22,21 +21,23 @@ public class InputCommandTest { public void run_missingArguments_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String dataString = ""; - Command testCommand = new InputCommand(dataString); - assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui)); + InputCommand testCommand = new InputCommand(dataString); + assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); } @Test public void run_insertData_insertedData() - throws CommandException, DataManagerException, UnknownModuleException, ModuleException { + throws CommandException, DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String dataString = "something"; - Command testCommand = new InputCommand(dataString); - testCommand.run(dataManager, moduleManager, ui); + InputCommand testCommand = new InputCommand(dataString); + testCommand.run(dataManager, moduleManager, ui, recipeManager); Data testData = new Data(dataString); assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index 84529148a1..2c1f7751e6 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -2,41 +2,40 @@ import static org.junit.jupiter.api.Assertions.assertTrue; -import decodex.data.exception.ModuleException; -import decodex.data.exception.UnknownModuleException; import org.junit.jupiter.api.Test; import java.util.Arrays; import decodex.data.Data; import decodex.data.DataManager; -import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; + import decodex.ui.Ui; class ResetCommandTest { @Test - public void run_noChanges_sameOriginalData() throws CommandException, DataManagerException, - UnknownModuleException, ModuleException { + public void run_noChanges_sameOriginalData() throws DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data originalData = new Data("hi"); dataManager.setOriginalData(originalData); - Command testCommand = new ResetCommand(); - testCommand.run(dataManager, moduleManager, ui); + ResetCommand testCommand = new ResetCommand(); + testCommand.run(dataManager, moduleManager, ui, recipeManager); assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); } @Test - public void run_newData_sameOriginalData() - throws CommandException, DataManagerException, UnknownModuleException, ModuleException { + public void run_newData_sameOriginalData() throws DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data originalData = new Data("hi"); @@ -44,8 +43,8 @@ public void run_newData_sameOriginalData() Data newData = new Data("bye"); dataManager.setCurrentData(newData); - Command testCommand = new ResetCommand(); - testCommand.run(dataManager, moduleManager, ui); + ResetCommand testCommand = new ResetCommand(); + testCommand.run(dataManager, moduleManager, ui, recipeManager); assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); } } diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index abf98b4315..fa07e57987 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -8,8 +8,11 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; import org.junit.jupiter.api.Test; @@ -18,9 +21,11 @@ public class SelectCommandTest { @Test public void run_validModuleName_success() - throws UnknownModuleException, CommandException, DataManagerException, ModuleException { + throws UnknownModuleException, CommandException, DataManagerException, ModuleException, RecipeException, + RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -28,15 +33,16 @@ public void run_validModuleName_success() String moduleName = "base64encode"; SelectCommand selectCommand = new SelectCommand(moduleName); - selectCommand.run(dataManager, moduleManager, ui); + selectCommand.run(dataManager, moduleManager, ui, recipeManager); assertEquals("aGVsbG8gd29ybGQ=", dataManager.getCurrentData().toString()); } @Test - public void run_blankModuleName_expectException() throws CommandException { + public void run_blankModuleName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -45,13 +51,14 @@ public void run_blankModuleName_expectException() throws CommandException { String moduleName = ""; SelectCommand selectCommand = new SelectCommand(moduleName); - assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); + assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); } @Test - public void run_unknownModuleName_expectException() throws CommandException { + public void run_unknownModuleName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -60,7 +67,7 @@ public void run_unknownModuleName_expectException() throws CommandException { String moduleName = "unknownModule"; SelectCommand selectCommand = new SelectCommand(moduleName); - assertThrows(UnknownModuleException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); + assertThrows(UnknownModuleException.class, + () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); } - } From 665df1d5c12ba5cf0d3023df0ce6ea334f68bfd0 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 20 Oct 2021 18:44:24 +0800 Subject: [PATCH 233/607] Add reference to current edited recipe --- .../java/decodex/recipes/RecipeManager.java | 39 +++- .../decodex/recipes/RecipeManagerTest.java | 185 ++++++++++++++++++ 2 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 src/test/java/decodex/recipes/RecipeManagerTest.java diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 31b3672cb5..5b1e83a6ab 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -1,7 +1,9 @@ package decodex.recipes; import decodex.Decodex; +import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.modules.Module; import java.util.HashMap; import java.util.logging.Logger; @@ -12,13 +14,15 @@ public class RecipeManager { private Logger logger = Decodex.logger; - HashMap recipeList; + private HashMap recipeList; + private String editedRecipeName; /** * Creates a new RecipeManager with no recipes. */ public RecipeManager() { recipeList = new HashMap<>(); + editedRecipeName = null; } /** @@ -44,7 +48,10 @@ public void addRecipe(Recipe recipe) throws RecipeManagerException { */ public Recipe removeRecipe(String name) throws RecipeManagerException { Recipe recipeToRemove = getRecipe(name); - recipeList.remove(recipeToRemove); + recipeList.remove(name); + if (editedRecipeName != null && editedRecipeName.equals(name)) { + editedRecipeName = null; + } logger.fine(String.format("[RecipeManager] Removed recipe %s", recipeToRemove.getName())); return recipeToRemove; } @@ -63,4 +70,32 @@ public Recipe getRecipe(String name) throws RecipeManagerException { return recipeList.get(name); } + public Recipe getEditedRecipe() throws RecipeManagerException { + if (editedRecipeName == null) { + throw new RecipeManagerException("No recipe selected"); + } + return getRecipe(editedRecipeName); + } + + public void selectRecipe(String name) throws RecipeManagerException { + if (!recipeList.containsKey(name)) { + throw new RecipeManagerException(RecipeManagerException.RECIPE_NOT_FOUND_MESSAGE); + } + editedRecipeName = name; + } + + public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerException { + Recipe editedRecipe = getEditedRecipe(); + editedRecipe.push(module); + } + + public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeException { + Recipe editedRecipe = getEditedRecipe(); + return editedRecipe.pop(); + } + + public void resetEditedRecipe() throws RecipeManagerException { + Recipe editedRecipe = getEditedRecipe(); + editedRecipe.reset(); + } } diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java new file mode 100644 index 0000000000..78912f9749 --- /dev/null +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -0,0 +1,185 @@ +package decodex.recipes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.exception.RecipeException; +import org.junit.jupiter.api.Test; + +import decodex.data.exception.RecipeManagerException; +import decodex.modules.hex.HexEncoder; +import decodex.modules.Module; + +class RecipeManagerTest { + + @Test + void addRecipe_repeatedRecipeName_expectException() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + Recipe newRecipe = new Recipe("sameRecipeName"); + recipeManager.addRecipe(newRecipe); + + Recipe repeatedRecipe = new Recipe("sameRecipeName"); + assertThrows(RecipeManagerException.class, () -> recipeManager.addRecipe(repeatedRecipe)); + } + + @Test + void removeRecipe_oneRecipe_removedRecipeIsTheOneRecipe() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + Recipe removedRecipe = recipeManager.removeRecipe(testRecipeName); + assertEquals(removedRecipe.getName(), testRecipeName); + } + + @Test + void removeRecipe_noRecipe_expectException() { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + assertThrows(RecipeManagerException.class, () -> recipeManager.removeRecipe(testRecipeName)); + } + + @Test + void removeRecipe_recipeIsEditedRecipe_expectException() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + recipeManager.selectRecipe(testRecipeName); + recipeManager.removeRecipe(testRecipeName); + assertThrows(RecipeManagerException.class, () -> recipeManager.getEditedRecipe()); + } + + @Test + void getRecipe_oneRecipe_managerContainOneRecipe() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + Recipe retrievedRecipe = recipeManager.getRecipe(testRecipeName); + assertEquals(testRecipe.getName(), retrievedRecipe.getName()); + } + + @Test + void getRecipe_noRecipe_expectException() { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + assertThrows(RecipeManagerException.class, () -> recipeManager.getRecipe(testRecipeName)); + } + + @Test + void getEditedRecipe_oneEditedRecipe_editedRecipe() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipe(testRecipeName); + + assertEquals(recipeManager.getEditedRecipe().getName(), testRecipeName); + } + + @Test + void getEditedRecipe_noEditedRecipe_expectException() { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + + assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipe(testRecipeName)); + } + + @Test + void selectRecipe_oneEditedRecipe_editedRecipe() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + recipeManager.selectRecipe(testRecipeName); + assertEquals(recipeManager.getEditedRecipe().getName(), testRecipeName); + } + + @Test + void selectRecipe_nonExistentRecipeName_expectException() { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + + assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipe(testRecipeName)); + } + + @Test + void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipe(testRecipeName); + + HexEncoder insertedModule = new HexEncoder(); + + recipeManager.pushModuleIntoEditedRecipe(insertedModule); + + assertEquals(recipeManager.getEditedRecipe().getModuleList().size(), 1); + } + + @Test + void pushModuleIntoEditedRecipe_noEditedRecipe_expectException() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + Module insertedModule = new HexEncoder(); + + assertThrows(RecipeManagerException.class, () -> recipeManager.pushModuleIntoEditedRecipe(insertedModule)); + } + + @Test + void popModuleFromEditedRecipe_oneEditedRecipe_poppedModule() throws RecipeException, RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + Module insertedModule = new HexEncoder(); + testRecipe.push(insertedModule); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipe(testRecipeName); + + Module poppedModule = recipeManager.popModuleFromEditedRecipe(); + assertEquals(insertedModule.getName(), poppedModule.getName()); + } + + @Test + void popModuleFromEditedRecipe_noEditedRecipe_expectException() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + Module insertedModule = new HexEncoder(); + testRecipe.push(insertedModule); + recipeManager.addRecipe(testRecipe); + + assertThrows(RecipeManagerException.class, () -> recipeManager.popModuleFromEditedRecipe()); + } + + @Test + void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipe(testRecipeName); + + recipeManager.resetEditedRecipe(); + + assertEquals(recipeManager.getEditedRecipe().getModuleList().size(), 0); + } + + @Test + void resetEditedRecipe_noEditedRecipe_expectException() throws RecipeManagerException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "testRecipeName"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + assertThrows(RecipeManagerException.class, () -> recipeManager.resetEditedRecipe()); + } +} From 36beb5845e86473b7dd044e4aa91ce29594a708e Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 20 Oct 2021 18:52:28 +0800 Subject: [PATCH 234/607] Move exception messages to ErrorMessages.java --- .../decodex/data/exception/RecipeException.java | 3 --- .../data/exception/RecipeManagerException.java | 3 --- src/main/java/decodex/recipes/Recipe.java | 3 ++- src/main/java/decodex/recipes/RecipeManager.java | 14 ++++++++------ .../java/decodex/ui/messages/ErrorMessages.java | 6 ++++++ .../java/decodex/recipes/RecipeManagerTest.java | 2 +- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/java/decodex/data/exception/RecipeException.java b/src/main/java/decodex/data/exception/RecipeException.java index cd2c50f4de..f670135bbe 100644 --- a/src/main/java/decodex/data/exception/RecipeException.java +++ b/src/main/java/decodex/data/exception/RecipeException.java @@ -5,9 +5,6 @@ */ public class RecipeException extends Exception { - public static final String EMPTY_RECIPE_MESSAGE = "[x] The recipe is currently empty. There is no module to be " - + "removed."; - public RecipeException(String message) { super(message); } diff --git a/src/main/java/decodex/data/exception/RecipeManagerException.java b/src/main/java/decodex/data/exception/RecipeManagerException.java index 0b38f4b10c..68244d40c4 100644 --- a/src/main/java/decodex/data/exception/RecipeManagerException.java +++ b/src/main/java/decodex/data/exception/RecipeManagerException.java @@ -5,9 +5,6 @@ */ public class RecipeManagerException extends Exception { - public static final String DUPLICATE_RECIPE_NAME_MESSAGE = "[x] A recipe with the given name already exists."; - public static final String RECIPE_NOT_FOUND_MESSAGE = "[x] The given recipe could not be found."; - public RecipeManagerException(String message) { super(message); } diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index a3d005c79c..138f169650 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -6,6 +6,7 @@ import decodex.Decodex; import decodex.data.exception.RecipeException; import decodex.modules.Module; +import decodex.ui.messages.ErrorMessages; /** * The Recipe class manages a list of Modules to be run sequentially. @@ -58,7 +59,7 @@ public void push(Module module) { */ public Module pop() throws RecipeException { if (moduleList.isEmpty()) { - throw new RecipeException(RecipeException.EMPTY_RECIPE_MESSAGE); + throw new RecipeException(ErrorMessages.EMPTY_RECIPE_MESSAGE); } Module removedModule = moduleList.remove(moduleList.size() - 1); diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 5b1e83a6ab..765fdd3efc 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -1,11 +1,13 @@ package decodex.recipes; +import java.util.HashMap; +import java.util.logging.Logger; + import decodex.Decodex; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.Module; -import java.util.HashMap; -import java.util.logging.Logger; +import decodex.ui.messages.ErrorMessages; /** * RecipeManager manages a list of all recipes that were created/loaded. @@ -33,7 +35,7 @@ public RecipeManager() { */ public void addRecipe(Recipe recipe) throws RecipeManagerException { if (recipeList.containsKey(recipe.getName())) { - throw new RecipeManagerException(RecipeManagerException.DUPLICATE_RECIPE_NAME_MESSAGE); + throw new RecipeManagerException(ErrorMessages.DUPLICATE_RECIPE_NAME_MESSAGE); } recipeList.put(recipe.getName(), recipe); logger.fine(String.format("[RecipeManager] Added recipe %s", recipe.getName())); @@ -65,21 +67,21 @@ public Recipe removeRecipe(String name) throws RecipeManagerException { */ public Recipe getRecipe(String name) throws RecipeManagerException { if (!recipeList.containsKey(name)) { - throw new RecipeManagerException(RecipeManagerException.RECIPE_NOT_FOUND_MESSAGE); + throw new RecipeManagerException(ErrorMessages.RECIPE_NOT_FOUND_MESSAGE); } return recipeList.get(name); } public Recipe getEditedRecipe() throws RecipeManagerException { if (editedRecipeName == null) { - throw new RecipeManagerException("No recipe selected"); + throw new RecipeManagerException(ErrorMessages.EDITED_RECIPE_NOT_FOUND_MESSAGE); } return getRecipe(editedRecipeName); } public void selectRecipe(String name) throws RecipeManagerException { if (!recipeList.containsKey(name)) { - throw new RecipeManagerException(RecipeManagerException.RECIPE_NOT_FOUND_MESSAGE); + throw new RecipeManagerException(ErrorMessages.RECIPE_NOT_FOUND_MESSAGE); } editedRecipeName = name; } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index e5bf5d4640..3dc2604ecf 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -10,4 +10,10 @@ public class ErrorMessages { public static final String NO_DATA_FOUND = "No data found"; public static final String BASE64_DECODING_FAILED_MESSAGE = "Your data does not seem to be in Base64 format"; public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; + public static final String EMPTY_RECIPE_MESSAGE = "The recipe is currently empty. There is no module to be " + + "removed."; + public static final String DUPLICATE_RECIPE_NAME_MESSAGE = "A recipe with the given name already exists."; + public static final String RECIPE_NOT_FOUND_MESSAGE = "The given recipe could not be found."; + public static final String EDITED_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected. Please select using the " + + "\"recipe select\" command."; } diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index 78912f9749..0aa766815d 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -3,10 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.data.exception.RecipeException; import org.junit.jupiter.api.Test; import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.RecipeException; import decodex.modules.hex.HexEncoder; import decodex.modules.Module; From 8232cacfa946d114097431081b15a7ee253ac6e8 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 20 Oct 2021 20:41:34 +0800 Subject: [PATCH 235/607] Move return of null into a guard clause --- src/main/java/decodex/parser/Parser.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 2d11a6d987..632594319a 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -98,10 +98,10 @@ public String[] getModuleParameters(String userInput) { String strippedUserInput = userInput.stripLeading(); String[] tokens = strippedUserInput.split(SPLIT_REGEX); String[] parameters = Arrays.copyOfRange(tokens, MODULE_PARAMETERS_INDEX, tokens.length); - if (parameters.length > 0) { - return parameters; + if (parameters.length == 0) { + return null; } - return null; + return parameters; } /** From 38b4e9b38672d1e658ebe07a90447ca554ff5bbb Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 20 Oct 2021 22:08:56 +0800 Subject: [PATCH 236/607] Add requested changes * Change recipe name to `editingRecipeName` * Add missing Javadocs * Update exception message * Update method names to reflect new recipe name --- .../java/decodex/recipes/RecipeManager.java | 69 ++++++++++++++----- .../decodex/ui/messages/ErrorMessages.java | 3 +- .../decodex/recipes/RecipeManagerTest.java | 26 +++---- 3 files changed, 66 insertions(+), 32 deletions(-) diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 765fdd3efc..8f2d91b335 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -16,15 +16,22 @@ public class RecipeManager { private Logger logger = Decodex.logger; + /** + * Stores mapping between recipe name and recipe. + */ private HashMap recipeList; - private String editedRecipeName; + + /** + * Name of the recipe that is selected for editing/modification. + */ + private String editingRecipeName; /** * Creates a new RecipeManager with no recipes. */ public RecipeManager() { recipeList = new HashMap<>(); - editedRecipeName = null; + editingRecipeName = null; } /** @@ -51,15 +58,15 @@ public void addRecipe(Recipe recipe) throws RecipeManagerException { public Recipe removeRecipe(String name) throws RecipeManagerException { Recipe recipeToRemove = getRecipe(name); recipeList.remove(name); - if (editedRecipeName != null && editedRecipeName.equals(name)) { - editedRecipeName = null; + if (editingRecipeName != null && editingRecipeName.equals(name)) { + editingRecipeName = null; } logger.fine(String.format("[RecipeManager] Removed recipe %s", recipeToRemove.getName())); return recipeToRemove; } /** - * Get a Recipe object from the recipe manager by name. + * Gets a Recipe object from the recipe manager by name. * * @param name The name of the recipe to get. * @return The recipe that was found. @@ -72,32 +79,60 @@ public Recipe getRecipe(String name) throws RecipeManagerException { return recipeList.get(name); } - public Recipe getEditedRecipe() throws RecipeManagerException { - if (editedRecipeName == null) { - throw new RecipeManagerException(ErrorMessages.EDITED_RECIPE_NOT_FOUND_MESSAGE); + /** + * Gets the Recipe object that is currently being edited. + * + * @return The current recipe being edited. + * @throws RecipeManagerException If no recipe is selected as currently being edited. + */ + public Recipe getEditingRecipe() throws RecipeManagerException { + if (editingRecipeName == null) { + throw new RecipeManagerException(ErrorMessages.EDITING_RECIPE_NOT_FOUND_MESSAGE); } - return getRecipe(editedRecipeName); + return getRecipe(editingRecipeName); } - public void selectRecipe(String name) throws RecipeManagerException { + /** + * Sets the recipe of the given name to be the recipe currently being edited. + * @param name The name of the recipe to select. + * @throws RecipeManagerException If the recipe could not be found in the recipe manager. + */ + public void selectRecipeForEditing(String name) throws RecipeManagerException { if (!recipeList.containsKey(name)) { throw new RecipeManagerException(ErrorMessages.RECIPE_NOT_FOUND_MESSAGE); } - editedRecipeName = name; + editingRecipeName = name; } + /** + * Appends a new module to the end of the recipe currently being edited. + * + * @param module The module to be added to the recipe. + * @throws RecipeManagerException If no recipe is selected as currently being edited. + */ public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerException { - Recipe editedRecipe = getEditedRecipe(); - editedRecipe.push(module); + Recipe editingRecipe = getEditingRecipe(); + editingRecipe.push(module); } + /** + * Removes the latest module from the recipe currently being edited. + * + * @return The module that was popped from the recipe. + * @throws RecipeManagerException If no recipe is selected as currently being edited. + */ public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeException { - Recipe editedRecipe = getEditedRecipe(); - return editedRecipe.pop(); + Recipe editingRecipe = getEditingRecipe(); + return editingRecipe.pop(); } + /** + * Completely removes all the modules from the recipe currently being edited. + * + * @throws RecipeManagerException If no recipe is selected as currently being edited. + */ public void resetEditedRecipe() throws RecipeManagerException { - Recipe editedRecipe = getEditedRecipe(); - editedRecipe.reset(); + Recipe editingRecipe = getEditingRecipe(); + editingRecipe.reset(); } } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 3dc2604ecf..e3f318ffa5 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -14,6 +14,5 @@ public class ErrorMessages { + "removed."; public static final String DUPLICATE_RECIPE_NAME_MESSAGE = "A recipe with the given name already exists."; public static final String RECIPE_NOT_FOUND_MESSAGE = "The given recipe could not be found."; - public static final String EDITED_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected. Please select using the " - + "\"recipe select\" command."; + public static final String EDITING_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected for editing/modification."; } diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index 0aa766815d..ab6e0e90c4 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -46,9 +46,9 @@ void removeRecipe_recipeIsEditedRecipe_expectException() throws RecipeManagerExc Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipe(testRecipeName); + recipeManager.selectRecipeForEditing(testRecipeName); recipeManager.removeRecipe(testRecipeName); - assertThrows(RecipeManagerException.class, () -> recipeManager.getEditedRecipe()); + assertThrows(RecipeManagerException.class, () -> recipeManager.getEditingRecipe()); } @Test @@ -75,9 +75,9 @@ void getEditedRecipe_oneEditedRecipe_editedRecipe() throws RecipeManagerExceptio String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipe(testRecipeName); + recipeManager.selectRecipeForEditing(testRecipeName); - assertEquals(recipeManager.getEditedRecipe().getName(), testRecipeName); + assertEquals(recipeManager.getEditingRecipe().getName(), testRecipeName); } @Test @@ -85,7 +85,7 @@ void getEditedRecipe_noEditedRecipe_expectException() { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; - assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipe(testRecipeName)); + assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipeForEditing(testRecipeName)); } @Test @@ -95,8 +95,8 @@ void selectRecipe_oneEditedRecipe_editedRecipe() throws RecipeManagerException { Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipe(testRecipeName); - assertEquals(recipeManager.getEditedRecipe().getName(), testRecipeName); + recipeManager.selectRecipeForEditing(testRecipeName); + assertEquals(recipeManager.getEditingRecipe().getName(), testRecipeName); } @Test @@ -104,7 +104,7 @@ void selectRecipe_nonExistentRecipeName_expectException() { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; - assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipe(testRecipeName)); + assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipeForEditing(testRecipeName)); } @Test @@ -113,13 +113,13 @@ void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() throws RecipeManage String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipe(testRecipeName); + recipeManager.selectRecipeForEditing(testRecipeName); HexEncoder insertedModule = new HexEncoder(); recipeManager.pushModuleIntoEditedRecipe(insertedModule); - assertEquals(recipeManager.getEditedRecipe().getModuleList().size(), 1); + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 1); } @Test @@ -142,7 +142,7 @@ void popModuleFromEditedRecipe_oneEditedRecipe_poppedModule() throws RecipeExcep Module insertedModule = new HexEncoder(); testRecipe.push(insertedModule); recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipe(testRecipeName); + recipeManager.selectRecipeForEditing(testRecipeName); Module poppedModule = recipeManager.popModuleFromEditedRecipe(); assertEquals(insertedModule.getName(), poppedModule.getName()); @@ -166,11 +166,11 @@ void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeManagerException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipe(testRecipeName); + recipeManager.selectRecipeForEditing(testRecipeName); recipeManager.resetEditedRecipe(); - assertEquals(recipeManager.getEditedRecipe().getModuleList().size(), 0); + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); } @Test From 836fd7e5114eb574c6a46d69b3cfe96c9a82f1a5 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 20 Oct 2021 22:45:11 +0800 Subject: [PATCH 237/607] Minor changes * Zero parameter modules will throw exception when receiving parameters * Add binary modules * Update tests --- .../java/decodex/modules/ModuleManager.java | 69 ++++++++++++++++--- src/main/java/decodex/parser/Parser.java | 5 +- .../decodex/commands/SelectCommandTest.java | 6 +- .../decodex/modules/ModuleManagerTest.java | 17 +++-- 4 files changed, 75 insertions(+), 22 deletions(-) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 16cad359c2..9e65d732d1 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -24,15 +24,20 @@ public class ModuleManager { private static final int MODULE_NAME_INDEX = 0; /** - * Available modules. + * List of available modules. */ private static final String[][] MODULE_LIST = { {Base64Encoder.MODULE_NAME, Base64Encoder.MODULE_DESCRIPTION}, - {HexEncoder.MODULE_NAME, HexDecoder.MODULE_DESCRIPTION}, - {BinaryEncoder.MODULE_NAME, BinaryDecoder.MODULE_DESCRIPTION}, + {Base64Decoder.MODULE_NAME, Base64Decoder.MODULE_DESCRIPTION}, + {HexEncoder.MODULE_NAME, HexEncoder.MODULE_DESCRIPTION}, + {HexDecoder.MODULE_NAME, HexDecoder.MODULE_DESCRIPTION}, + {BinaryEncoder.MODULE_NAME, BinaryEncoder.MODULE_DESCRIPTION}, + {BinaryDecoder.MODULE_NAME, BinaryDecoder.MODULE_DESCRIPTION}, {RotEncoder.MODULE_NAME, RotEncoder.MODULE_DESCRIPTION} }; + private static final int MODULE_PARAMETER_INDEX = 0; + public ModuleManager() { // Log available modules for (String[] module : MODULE_LIST) { @@ -45,30 +50,44 @@ public String[][] getModules() { } /** - * Select module from a provided module name. + * Selects module from a provided module name. * * @param moduleName Name of selected module. * @param parameters Parameters for specific commands. * @return Selected module. * @throws UnknownModuleException If provided module name is not an available module. - * @throws ModuleException If module parameters are invalid. + * @throws ModuleException If module parameters are invalid. */ public Module selectModule(String moduleName, String[] parameters) throws UnknownModuleException, ModuleException { + assert parameters != null : "parameters should not be null"; + Module module; switch (moduleName) { case Base64Encoder.MODULE_NAME: + hasNoParameters(parameters); module = prepareBase64Encoder(); break; case Base64Decoder.MODULE_NAME: + hasNoParameters(parameters); module = prepareBase64Decoder(); break; case HexEncoder.MODULE_NAME: + hasNoParameters(parameters); module = prepareHexEncoder(); break; case HexDecoder.MODULE_NAME: + hasNoParameters(parameters); module = prepareHexDecoder(); break; + case BinaryEncoder.MODULE_NAME: + hasNoParameters(parameters); + module = prepareBinaryEncoder(); + break; + case BinaryDecoder.MODULE_NAME: + hasNoParameters(parameters); + module = prepareBinaryDecoder(); + break; case RotEncoder.MODULE_NAME: module = prepareRotEncoder(parameters); break; @@ -80,6 +99,18 @@ public Module selectModule(String moduleName, String[] parameters) throws Unknow return module; } + /** + * Throws ModuleException if the module parameters are empty. + * + * @param parameters Parameters for specific commands. + * @throws ModuleException If the String array of module parameters is not empty. + */ + private void hasNoParameters(String[] parameters) throws ModuleException { + if (parameters.length > 0) { + throw new ModuleException(ErrorMessages.TOO_MANY_MODULE_PARAMETERS); + } + } + /** * Prepares and returns the Base64Encoder. * @@ -116,6 +147,24 @@ private Module prepareHexDecoder() { return new HexDecoder(); } + /** + * Prepares and returns the BinaryEncoder. + * + * @return The BinaryEncoder object. + */ + private Module prepareBinaryEncoder() { + return new BinaryEncoder(); + } + + /** + * Prepares and returns the BinaryDecoder. + * + * @return The BinaryDecoder object. + */ + private Module prepareBinaryDecoder() { + return new BinaryDecoder(); + } + /** * Prepares and returns the RotEncoder with specified rotation. * @@ -124,18 +173,16 @@ private Module prepareHexDecoder() { * @throws ModuleException If the number of parameters are incorrect or invalid. */ private Module prepareRotEncoder(String[] parameters) throws ModuleException { - if (parameters == null) { + if (parameters.length == 0) { throw new ModuleException(ErrorMessages.MISSING_MODULE_PARAMETERS); - } - - if (parameters.length > 1) { + } else if (parameters.length > 1) { throw new ModuleException(ErrorMessages.TOO_MANY_MODULE_PARAMETERS); } // First parameter is the rotation offset try { - return new RotEncoder(Integer.parseInt(parameters[0])); - } catch (NumberFormatException e) { + return new RotEncoder(Integer.parseInt(parameters[MODULE_PARAMETER_INDEX])); + } catch (NumberFormatException err) { throw new ModuleException(ErrorMessages.INVALID_MODULE_PARAMETERS); } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 632594319a..15fff55b70 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -92,15 +92,12 @@ public String getModuleName(String userInput) throws ParserException { * Returns the module parameters that the user has specified. * * @param userInput The input specified by the user. - * @return A String array of module parameters if provided. Otherwise, returns null. + * @return A String array of module parameters. */ public String[] getModuleParameters(String userInput) { String strippedUserInput = userInput.stripLeading(); String[] tokens = strippedUserInput.split(SPLIT_REGEX); String[] parameters = Arrays.copyOfRange(tokens, MODULE_PARAMETERS_INDEX, tokens.length); - if (parameters.length == 0) { - return null; - } return parameters; } diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index 0effe0069f..76f9d46ecf 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -27,7 +27,7 @@ public void run_validModuleName_success() dataManager.setOriginalData(data); String moduleName = "base64encode"; - String[] parameters = null; + String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(moduleName, parameters); selectCommand.run(dataManager, moduleManager, ui); @@ -62,7 +62,7 @@ public void run_blankModuleName_expectException() throws CommandException { dataManager.setOriginalData(data); String moduleName = ""; - String[] parameters = null; + String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(moduleName, parameters); assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); @@ -78,7 +78,7 @@ public void run_unknownModuleName_expectException() throws CommandException { dataManager.setOriginalData(data); String moduleName = "unknownModule"; - String[] parameters = null; + String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(moduleName, parameters); assertThrows(UnknownModuleException.class, () -> selectCommand.run(dataManager, moduleManager, ui)); diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index ba9e360bc2..d0aa61c5fc 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -15,7 +15,7 @@ public class ModuleManagerTest { @Test public void selectModule_base64encode_success() throws UnknownModuleException, ModuleException { String moduleName = "base64encode"; - String[] parameters = null; + String[] parameters = {}; ModuleManager moduleManager = new ModuleManager(); Module module = moduleManager.selectModule(moduleName, parameters); @@ -35,7 +35,7 @@ public void selectModule_rotEncodeWithValidParameter_success() throws UnknownMod @Test public void selectModule_unknownModuleName_unknownModuleExceptionThrown() { String moduleName = "unknownModule"; - String[] parameters = null; + String[] parameters = {}; ModuleManager moduleManager = new ModuleManager(); assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); } @@ -43,15 +43,24 @@ public void selectModule_unknownModuleName_unknownModuleExceptionThrown() { @Test public void selectModule_blankModuleName_unknownModuleExceptionThrown() { String moduleName = ""; - String[] parameters = null; + String[] parameters = {}; ModuleManager moduleManager = new ModuleManager(); assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); } + @Test + public void selectModule_zeroParameterModuleWithParameters_moduleExceptionThrown() { + String moduleName = "base64encode"; + String[] parameters = {"1"}; + ModuleManager moduleManager = new ModuleManager(); + + assertThrows(ModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); + } + @Test public void selectModule_rotEncodeWithoutParameters_success() { String moduleName = "rotencode"; - String[] parameters = null; + String[] parameters = {}; ModuleManager moduleManager = new ModuleManager(); assertThrows(ModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); From f1926cf7a3151dbc7d1a73359c319ba248d8e3de Mon Sep 17 00:00:00 2001 From: rizemon Date: Thu, 21 Oct 2021 11:02:01 +0800 Subject: [PATCH 238/607] Update DG --- docs/DeveloperGuide.md | 66 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..786b9e913f 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,38 +1,32 @@ # Developer Guide -## Acknowledgements - -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} - -## Design & implementation - -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} - - -## Product scope -### Target user profile - -{Describe the target user profile} - -### Value proposition - -{Describe the value proposition: what problem does it solve?} - -## User Stories - -|Version| As a ... | I want to ... | So that I can ...| -|--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| - -## Non-Functional Requirements - -{Give non-functional requirements} - -## Glossary - -* *glossary item* - Definition - -## Instructions for manual testing - -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +# Acknowledgements + +1. SE-EDU + 1. [AB3 Developer Guide Format](https://se-education.org/addressbook-level3/DeveloperGuide.html) + 2. [Setting up and getting started page and related links](https://se-education.org/addressbook-level3/SettingUp.html) + 3. [AB2 Code Structure](https://github.com/se-edu/addressbook-level2) + +# Setting Up, Getting Started + +## Setting up the on your computer + +1. Forking and cloning + 1. **Fork** our repository. + 2. Then, **clone** the fork onto your computer. + +> For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). +> +1. Editor + 1. We highly recommend using Intellij IDEA, which can be downloaded from [here](https://www.jetbrains.com/idea/). + 2. However, you may still use other editors that you prefer. Just take note that most of our set ups are centered around Intellij. +2. Configure JDK + 1. Follow the guide at *[[se-edu/guides] IDEA: Configuring the JDK](https://se-education.org/guides/tutorials/intellijJdk.html)* to ensure Intellij is configured to use **JDK 11**. +3. Importing project + 1. Follow the guide at *[[se-edu/guides] IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html)* to import the forked project into Intellij. + +> Note: Importing a Gradle project is slightly different from importing a normal Java project. +> +1. Verifying setup + 1. Run the `decodex.Decodex.java` and try a few commands. + 2. [Run the tests](https://se-education.org/addressbook-level3/Testing.html) to ensure they all pass. \ No newline at end of file From e830529a2851121b44a328b5f28151ab7decc9ad Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:09:55 +0800 Subject: [PATCH 239/607] Add content headers for design and appendix sections in developer guide --- docs/DeveloperGuide.md | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..4e99c14818 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,38 +1,29 @@ -# Developer Guide +# Decodex V2.0 Developer Guide -## Acknowledgements +# Design -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +## Architecture -## Design & implementation +## UI Component -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +## Logic Component +## Data Component -## Product scope -### Target user profile +## Module Component -{Describe the target user profile} +## Recipe Component -### Value proposition +## Storage Component -{Describe the value proposition: what problem does it solve?} +# Documentation, Logging, Testing, Configuration and Dev-Ops -## User Stories +# Appendix A: Product Scope -|Version| As a ... | I want to ... | So that I can ...| -|--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| +# Appendix B: User Stories -## Non-Functional Requirements +# Appendix C: Non Functional Requirements -{Give non-functional requirements} +# Appendix D: Glossary -## Glossary - -* *glossary item* - Definition - -## Instructions for manual testing - -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +# Appendix E: Instructions for Manual Testing From 07839275cd59ac4c55c22a41514bdd87689220c1 Mon Sep 17 00:00:00 2001 From: rizemon Date: Thu, 21 Oct 2021 11:10:02 +0800 Subject: [PATCH 240/607] Update heading sizes --- docs/DeveloperGuide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 786b9e913f..d6db99dd3c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,15 +1,15 @@ # Developer Guide -# Acknowledgements +## Acknowledgements 1. SE-EDU 1. [AB3 Developer Guide Format](https://se-education.org/addressbook-level3/DeveloperGuide.html) 2. [Setting up and getting started page and related links](https://se-education.org/addressbook-level3/SettingUp.html) 3. [AB2 Code Structure](https://github.com/se-edu/addressbook-level2) -# Setting Up, Getting Started +## Setting Up, Getting Started -## Setting up the on your computer +### Setting up the on your computer 1. Forking and cloning 1. **Fork** our repository. From 60d129f0c6cff76354039f32106d34f3d512b28d Mon Sep 17 00:00:00 2001 From: rizemon Date: Thu, 21 Oct 2021 11:18:08 +0800 Subject: [PATCH 241/607] Update header size --- docs/UserGuide.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 456d85507f..fe297e4dd1 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,6 +1,6 @@ # Decodex User Guide -# Introduction +## Introduction Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly transform data from one format to another with extreme ease**. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. @@ -8,7 +8,7 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CT ![carbon(6).png](images/carbon(6).png) -# Table of Contents +## Table of Contents - [Introduction](#introduction) - [Terminologies](#terminologies) - [Features](#features) @@ -19,7 +19,7 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CT - [Exiting the program: `exit`](#exiting-the-program-exit) - [Command Summary](#command-summary) -# Terminologies +## Terminologies This section serves to help the user better understand the terminologies used in this user guide. @@ -30,9 +30,9 @@ This section serves to help the user better understand the terminologies used in | Base64, Hexadecimal | Common types of data encoding standards. | | Module | A self-contained set of instructions to process data into another form. | -# Features +## Features -## Input of data: `input` +### Input of data: `input` Stores the data to be processed by modules. @@ -51,7 +51,7 @@ Examples: > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. -## List available modules: `list` +### List available modules: `list` Shows a list of all available modules. @@ -59,7 +59,7 @@ Format: `list` ![carbon(8).png](images/carbon(8).png) -## Selection of module: `select` +### Selection of module: `select` Selects a module and processes the data accordingly. Subsequent selection of modules will process the transformed data output from the previous module. @@ -76,19 +76,19 @@ Examples: > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. -## Resetting of data: `reset` +### Resetting of data: `reset` Resets the transformed data back to the original input. Format: `reset` -## Exiting the program: `exit` +### Exiting the program: `exit` Exit the program. Format: `exit` -# Command Summary +## Command Summary | Action | Command Format | Example Usage | | ------------------------ | --------------------- | ------------------------------------------------------------ | From c0d7e70c27aa2356de62b58a5ff3440348c62733 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:20:39 +0800 Subject: [PATCH 242/607] Update content headers to start with h2 --- docs/DeveloperGuide.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 4e99c14818..8b7ef1d18b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,29 +1,29 @@ -# Decodex V2.0 Developer Guide +# Decodex Developer Guide -# Design +## Design -## Architecture +### Architecture -## UI Component +### UI Component -## Logic Component +### Logic Component -## Data Component +### Data Component -## Module Component +### Module Component -## Recipe Component +### Recipe Component -## Storage Component +### Storage Component -# Documentation, Logging, Testing, Configuration and Dev-Ops +## Documentation, Logging, Testing, Configuration and Dev-Ops -# Appendix A: Product Scope +## Appendix A: Product Scope -# Appendix B: User Stories +## Appendix B: User Stories -# Appendix C: Non Functional Requirements +## Appendix C: Non Functional Requirements -# Appendix D: Glossary +## Appendix D: Glossary -# Appendix E: Instructions for Manual Testing +## Appendix E: Instructions for Manual Testing From c305a046cd7a9901b192d831dded05409420ca21 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:42:55 +0800 Subject: [PATCH 243/607] Move index constants together --- src/main/java/decodex/modules/ModuleManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 9e65d732d1..2708f4eb55 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -22,6 +22,7 @@ public class ModuleManager { private final Logger logger = Decodex.logger; private static final int MODULE_NAME_INDEX = 0; + private static final int MODULE_PARAMETER_INDEX = 0; /** * List of available modules. @@ -36,8 +37,6 @@ public class ModuleManager { {RotEncoder.MODULE_NAME, RotEncoder.MODULE_DESCRIPTION} }; - private static final int MODULE_PARAMETER_INDEX = 0; - public ModuleManager() { // Log available modules for (String[] module : MODULE_LIST) { From 44946a8721449107b5844f868f2b2e368f994eb0 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:44:42 +0800 Subject: [PATCH 244/607] Move checking of module parameters into individual prepare methods --- .../java/decodex/modules/ModuleManager.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index 2708f4eb55..dfbd10a98a 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -64,28 +64,22 @@ public Module selectModule(String moduleName, String[] parameters) throws Unknow switch (moduleName) { case Base64Encoder.MODULE_NAME: - hasNoParameters(parameters); - module = prepareBase64Encoder(); + module = prepareBase64Encoder(parameters); break; case Base64Decoder.MODULE_NAME: - hasNoParameters(parameters); - module = prepareBase64Decoder(); + module = prepareBase64Decoder(parameters); break; case HexEncoder.MODULE_NAME: - hasNoParameters(parameters); - module = prepareHexEncoder(); + module = prepareHexEncoder(parameters); break; case HexDecoder.MODULE_NAME: - hasNoParameters(parameters); - module = prepareHexDecoder(); + module = prepareHexDecoder(parameters); break; case BinaryEncoder.MODULE_NAME: - hasNoParameters(parameters); - module = prepareBinaryEncoder(); + module = prepareBinaryEncoder(parameters); break; case BinaryDecoder.MODULE_NAME: - hasNoParameters(parameters); - module = prepareBinaryDecoder(); + module = prepareBinaryDecoder(parameters); break; case RotEncoder.MODULE_NAME: module = prepareRotEncoder(parameters); @@ -114,8 +108,10 @@ private void hasNoParameters(String[] parameters) throws ModuleException { * Prepares and returns the Base64Encoder. * * @return The Base64Encoder object. + * @throws ModuleException If module parameters are provided. */ - private Module prepareBase64Encoder() { + private Module prepareBase64Encoder(String[] parameters) throws ModuleException { + hasNoParameters(parameters); return new Base64Encoder(); } @@ -123,8 +119,10 @@ private Module prepareBase64Encoder() { * Prepares and returns the Base64Decoder. * * @return The Base64Decoder object. + * @throws ModuleException If module parameters are provided. */ - private Module prepareBase64Decoder() { + private Module prepareBase64Decoder(String[] parameters) throws ModuleException { + hasNoParameters(parameters); return new Base64Decoder(); } @@ -132,8 +130,10 @@ private Module prepareBase64Decoder() { * Prepares and returns the HexEncoder. * * @return The HexEncoder object. + * @throws ModuleException If module parameters are provided. */ - private Module prepareHexEncoder() { + private Module prepareHexEncoder(String[] parameters) throws ModuleException { + hasNoParameters(parameters); return new HexEncoder(); } @@ -141,8 +141,10 @@ private Module prepareHexEncoder() { * Prepares and returns the HexDecoder. * * @return The HexDecoder object. + * @throws ModuleException If module parameters are provided. */ - private Module prepareHexDecoder() { + private Module prepareHexDecoder(String[] parameters) throws ModuleException { + hasNoParameters(parameters); return new HexDecoder(); } @@ -150,8 +152,10 @@ private Module prepareHexDecoder() { * Prepares and returns the BinaryEncoder. * * @return The BinaryEncoder object. + * @throws ModuleException If module parameters are provided. */ - private Module prepareBinaryEncoder() { + private Module prepareBinaryEncoder(String[] parameters) throws ModuleException { + hasNoParameters(parameters); return new BinaryEncoder(); } @@ -159,8 +163,10 @@ private Module prepareBinaryEncoder() { * Prepares and returns the BinaryDecoder. * * @return The BinaryDecoder object. + * @throws ModuleException If module parameters are provided. */ - private Module prepareBinaryDecoder() { + private Module prepareBinaryDecoder(String[] parameters) throws ModuleException { + hasNoParameters(parameters); return new BinaryDecoder(); } From b329261f0c7761727cc4779e44317839a94ca6f9 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 21 Oct 2021 11:46:06 +0800 Subject: [PATCH 245/607] Added Before Writing Code portion for DG --- docs/DeveloperGuide.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 221e7d218e..3b6741942f 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -29,7 +29,21 @@ 1. Verifying setup 1. Run the `decodex.Decodex.java` and try a few commands. 2. [Run the tests](https://se-education.org/addressbook-level3/Testing.html) to ensure they all pass. - + +### Before writing code + +1. Configure the coding style + 1. If using IDEA, follow the guide [[se-edu/guides] IDEA: Configuring the code style](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA’s coding style to match ours. + > :bulb: Tip: Optionally, you can follow the guide [[se-edu/guides] Using Checkstyle](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems as you write code. +2. Set up CI + 1. This project comes with a GitHub Actions config files (in `.github/workflows` folder). When GitHub detects those files, it will run the CI for your project automatically at each push to the `master` branch or to any PR. No set up required. +3. About our code structure + 1. OOP standardized + 2. Modularized. + 1. Any changes/additions to the current commands would simply require the changes within `Parser.java`. + 2. For any changes/additions to the modules, would simply require changes within the `src/main/java/decodex/modules` folder. + 3. This structure makes it easier for us as well as developers like you to maintain and further extend the capabilities of our application. + ## Design ### Architecture From 5101dbe6fa2613047a66c5c3852b597e94690b07 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 21 Oct 2021 11:57:29 +0800 Subject: [PATCH 246/607] Removed unnecessary ">" breaking the list --- docs/DeveloperGuide.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 3b6741942f..7847372a3c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -16,7 +16,7 @@ 2. Then, **clone** the fork onto your computer. > For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). -> + 1. Editor 1. We highly recommend using Intellij IDEA, which can be downloaded from [here](https://www.jetbrains.com/idea/). 2. However, you may still use other editors that you prefer. Just take note that most of our set ups are centered around Intellij. @@ -24,9 +24,8 @@ 1. Follow the guide at *[[se-edu/guides] IDEA: Configuring the JDK](https://se-education.org/guides/tutorials/intellijJdk.html)* to ensure Intellij is configured to use **JDK 11**. 3. Importing project 1. Follow the guide at *[[se-edu/guides] IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html)* to import the forked project into Intellij. -> Note: Importing a Gradle project is slightly different from importing a normal Java project. -> -1. Verifying setup + > Note: Importing a Gradle project is slightly different from importing a normal Java project. +4. Verifying setup 1. Run the `decodex.Decodex.java` and try a few commands. 2. [Run the tests](https://se-education.org/addressbook-level3/Testing.html) to ensure they all pass. From 4a8c45768ada3c134e3d8efa69299f9eac4ef16d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 21 Oct 2021 11:59:02 +0800 Subject: [PATCH 247/607] Testing - Removing the additional indents. --- docs/DeveloperGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 7847372a3c..c22a37c3a8 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -24,7 +24,7 @@ 1. Follow the guide at *[[se-edu/guides] IDEA: Configuring the JDK](https://se-education.org/guides/tutorials/intellijJdk.html)* to ensure Intellij is configured to use **JDK 11**. 3. Importing project 1. Follow the guide at *[[se-edu/guides] IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html)* to import the forked project into Intellij. - > Note: Importing a Gradle project is slightly different from importing a normal Java project. +> Note: Importing a Gradle project is slightly different from importing a normal Java project. 4. Verifying setup 1. Run the `decodex.Decodex.java` and try a few commands. 2. [Run the tests](https://se-education.org/addressbook-level3/Testing.html) to ensure they all pass. @@ -33,7 +33,7 @@ 1. Configure the coding style 1. If using IDEA, follow the guide [[se-edu/guides] IDEA: Configuring the code style](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA’s coding style to match ours. - > :bulb: Tip: Optionally, you can follow the guide [[se-edu/guides] Using Checkstyle](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems as you write code. +> :bulb: Tip: Optionally, you can follow the guide [[se-edu/guides] Using Checkstyle](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems as you write code. 2. Set up CI 1. This project comes with a GitHub Actions config files (in `.github/workflows` folder). When GitHub detects those files, it will run the CI for your project automatically at each push to the `master` branch or to any PR. No set up required. 3. About our code structure From bfdee2dde8d38ff4e0f64ac8cc3513a2ec27e3cc Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 21 Oct 2021 12:03:43 +0800 Subject: [PATCH 248/607] Test commit to check if indent breaks it. --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index c22a37c3a8..10412bd83e 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -15,7 +15,7 @@ 1. **Fork** our repository. 2. Then, **clone** the fork onto your computer. -> For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). + > For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). 1. Editor 1. We highly recommend using Intellij IDEA, which can be downloaded from [here](https://www.jetbrains.com/idea/). From 4aab55516c9a0607e4ffd4580c30e6d6f818be7b Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 14:09:38 +0800 Subject: [PATCH 249/607] Update `SelectCommandTest`'s `run()` with `RecipeManager` --- src/test/java/decodex/commands/SelectCommandTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index dbca50e251..a5001b55e4 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -41,10 +41,12 @@ public void run_validModuleName_success() @Test public void run_moduleWithParameters_success() - throws UnknownModuleException, CommandException, ModuleException, DataManagerException { + throws UnknownModuleException, CommandException, ModuleException, DataManagerException, RecipeException, + RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); + RecipeManager recipeManager = new RecipeManager(); Data data = new Data("hello world"); dataManager.setOriginalData(data); @@ -52,7 +54,7 @@ public void run_moduleWithParameters_success() String moduleName = "rotencode"; String[] parameters = {"13"}; SelectCommand selectCommand = new SelectCommand(moduleName, parameters); - selectCommand.run(dataManager, moduleManager, ui); + selectCommand.run(dataManager, moduleManager, ui, recipeManager); assertEquals("uryyb jbeyq", dataManager.getCurrentData().toString()); } @@ -89,6 +91,6 @@ public void run_unknownModuleName_expectException() { SelectCommand selectCommand = new SelectCommand(moduleName, parameters); assertThrows(UnknownModuleException.class, - () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); + () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); } } From b4ecd90287b6ad5581ea168f00241ba9443fac7a Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 14:13:55 +0800 Subject: [PATCH 250/607] Fix tests indentation and remove unused exception --- src/test/java/decodex/commands/SelectCommandTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index a5001b55e4..80f29347a0 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -60,7 +60,7 @@ public void run_moduleWithParameters_success() } @Test - public void run_blankModuleName_expectException() throws CommandException { + public void run_blankModuleName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); @@ -90,7 +90,7 @@ public void run_unknownModuleName_expectException() { String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(moduleName, parameters); - assertThrows(UnknownModuleException.class, - () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); + assertThrows(UnknownModuleException.class, () -> selectCommand.run(dataManager, moduleManager, ui, + recipeManager)); } } From e075df52a569e9ba97a337fd72285d9259c33020 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:52:58 +0800 Subject: [PATCH 251/607] Update developer guide with architecture information --- docs/DeveloperGuide.md | 19 +++++++++++++++++++ docs/images/architecture.png | Bin 0 -> 16497 bytes 2 files changed, 19 insertions(+) create mode 100644 docs/images/architecture.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 221e7d218e..1146c5eb37 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -34,6 +34,25 @@ ### Architecture +![Architecture diagram](images/architecture.png "Architecture diagram") + +The ***Architecture Diagram*** given above explains the high-level design of Decodex. + +Given below is a quick overview of the main components and how they interact with one another. + +**Main Components of the architecture** + +`Decodex` is responsible initialising the components at launch. + +The rest of the program consists of 6 other components: + +- `UI`: Handles user input and message output to the console +- `Logic`: Parses user input and executes commands +- `Recipe`: Manages a list of module sequences +- `Module`: Manages a set of encoding and decoding processes +- `Data`: Holds the data that is to be encoded or decoded +- `Storage`: Manages the reading and writing of data to disk + ### UI Component ### Logic Component diff --git a/docs/images/architecture.png b/docs/images/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..d8b9c7710d99639b3d586b43442c3e85b7597755 GIT binary patch literal 16497 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 Ry Date: Thu, 21 Oct 2021 18:36:56 +0800 Subject: [PATCH 252/607] Remove `/modification` from error messages --- src/main/java/decodex/ui/messages/ErrorMessages.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index e3f318ffa5..60caf4521a 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -14,5 +14,5 @@ public class ErrorMessages { + "removed."; public static final String DUPLICATE_RECIPE_NAME_MESSAGE = "A recipe with the given name already exists."; public static final String RECIPE_NOT_FOUND_MESSAGE = "The given recipe could not be found."; - public static final String EDITING_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected for editing/modification."; + public static final String EDITING_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected for editing."; } From 65f716d9008144f54fbb2ab4518087384c18e300 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Thu, 21 Oct 2021 19:30:14 +0800 Subject: [PATCH 253/607] Add DG table of contents --- docs/DeveloperGuide.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 10412bd83e..da3b4288c8 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,5 +1,34 @@ # Decodex Developer Guide +## Table of Contents + +- Acknowledgements +- Setting Up, Getting Started + - Setting Up + - Forking and cloning repository + - Editor + - Configure JDK + - Importing project + - Verifying setup + - Before writing code + - Configure the coding style + - Set up CI + - About our code structure +- Design + - Architecture + - UI Component + - Logic Component + - Data Component + - Module Component + - Recipe Component + - Storage Component +- Documentation, Logging, Testing, Configuration, Dev-Ops +- Appendix A: Product Scope +- Appendix B: User Stories +- Appendix C: Non Functional Requirements +- Appendix D: Glossary +- Appendix E: Instructions for Manual Testing (more details below) + ## Acknowledgements 1. SE-EDU From ca0af5d54d4e33720645a2f0f19fba5be865f3af Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 21:27:43 +0800 Subject: [PATCH 254/607] Update `ListCommand` to list recipes * Update `ListCommand` to take in `listCategory` as a constructor parameter * Update `Parser` to extract list category * Add a method in `RecipeManager` to return recipe names * Add `Ui` method to print recipes * Update messages * Add `ListCommandTest` * Update `ParserTest` --- .../java/decodex/commands/ListCommand.java | 56 +++++++++++++++++- src/main/java/decodex/parser/Parser.java | 34 +++++++++-- .../java/decodex/recipes/RecipeManager.java | 21 ++++++- src/main/java/decodex/ui/Ui.java | 19 ++++++- .../decodex/ui/messages/ErrorMessages.java | 2 + .../decodex/ui/messages/RegularMessages.java | 2 + .../decodex/commands/ListCommandTest.java | 47 +++++++++++++++ src/test/java/decodex/parser/ParserTest.java | 57 ++++++++++++++++--- 8 files changed, 217 insertions(+), 21 deletions(-) create mode 100644 src/test/java/decodex/commands/ListCommandTest.java diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 5b37349ab4..7015443ae6 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -1,22 +1,53 @@ package decodex.commands; import decodex.data.DataManager; +import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; + public static final String LIST_CATEGORY_MODULES = "modules"; + public static final String LIST_CATEGORY_RECIPE = "recipes"; private static final int MODULE_NAME_INDEX = 0; private static final int MODULE_DESCRIPTION_INDEX = 1; - public ListCommand() { + private final String listCategory; + + public ListCommand(String listCategory) { super(); + if (listCategory != null) { + listCategory = listCategory.strip(); + } + this.listCategory = listCategory; } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException { + if (listCategory == null) { + // Print both module and recipe list + ui.printModuleList(getModuleList(moduleManager)); + ui.printRecipeList(getRecipeList(recipeManager)); + } else if (listCategory.equals(LIST_CATEGORY_MODULES)) { + ui.printModuleList(getModuleList(moduleManager)); + } else if (listCategory.equals(LIST_CATEGORY_RECIPE)) { + ui.printRecipeList(getRecipeList(recipeManager)); + } else { + throw new CommandException(ErrorMessages.INVALID_LIST_CATEGORY); + } + } + + /** + * Prepares the list of supported modules. + * + * @param moduleManager The ModuleManager to retrieve supported modules. + * @return A formatted String of modules. + */ + private String getModuleList(ModuleManager moduleManager) { String[][] modules = moduleManager.getModules(); assert modules.length > 0 : "Number of modules should be greater than 0"; @@ -38,6 +69,25 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec moduleListString.append(String.format(" %-" + maxNameWidth + "s - %s\n", moduleName, moduleDescription)); } - ui.printModuleList(moduleListString.toString()); + return moduleListString.toString(); + } + + /** + * Prepares the list of available modules. + * + * @param recipeManager The RecipeManager to retrieve available recipes. + * @return A formatted String of recipes. + */ + private String getRecipeList(RecipeManager recipeManager) { + String[] recipeNames = recipeManager.getRecipeNames(); + + StringBuilder recipeListString = new StringBuilder(); + + // Prepare and format list of recipes + for (String recipeName : recipeNames) { + recipeListString.append(String.format(" %s\n", recipeName)); + } + + return recipeListString.toString(); } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 15fff55b70..916b5ac904 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -8,6 +8,7 @@ import decodex.commands.ListCommand; import decodex.commands.ResetCommand; import decodex.commands.SelectCommand; +import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; import decodex.ui.messages.ErrorMessages; @@ -125,9 +126,10 @@ public String getInputString(String userInput) throws ParserException { * * @param userInput The user input specified by the user. * @return The corresponding Command object. - * @throws ParserException If the command type is unknown or invalid. + * @throws ParserException If the command type is unknown or invalid. + * @throws CommandException If the command has invalid number of arguments. */ - public Command parseCommand(String userInput) throws ParserException { + public Command parseCommand(String userInput) throws ParserException, CommandException { Command command; String commandType = getCommandType(userInput); @@ -139,7 +141,7 @@ public Command parseCommand(String userInput) throws ParserException { command = craftInputCommand(userInput); break; case ListCommand.COMMAND_WORD: - command = craftListCommand(); + command = craftListCommand(userInput); break; case ResetCommand.COMMAND_WORD: command = craftResetCommand(); @@ -174,13 +176,35 @@ private InputCommand craftInputCommand(String userInput) throws ParserException return new InputCommand(inputData); } + /** + * Returns the list category for the list command. + * + * @param userInput The user input specified by the user. + * @return The list category String. + * @throws CommandException If the command has more than 1 argument. + */ + private String getListCategory(String userInput) throws CommandException { + String[] arguments = userInput.split(SPLIT_REGEX); + assert arguments.length > 0 : "userInput should have command word"; + + if (arguments.length == 1) { + return null; + } else if (arguments.length == 2) { + return arguments[STARTING_ARGUMENTS_INDEX]; + } else { + throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); + } + } + /** * Prepares and returns the ListCommand. * * @return The ListCommand object. + * @throws CommandException If the number of command argument is invalid. */ - private ListCommand craftListCommand() { - return new ListCommand(); + private ListCommand craftListCommand(String userInput) throws CommandException { + String listCategory = getListCategory(userInput); + return new ListCommand(listCategory); } /** diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 31b3672cb5..e0e7458510 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -1,10 +1,12 @@ package decodex.recipes; -import decodex.Decodex; -import decodex.data.exception.RecipeManagerException; +import java.util.Arrays; import java.util.HashMap; import java.util.logging.Logger; +import decodex.Decodex; +import decodex.data.exception.RecipeManagerException; + /** * RecipeManager manages a list of all recipes that were created/loaded. */ @@ -12,7 +14,9 @@ public class RecipeManager { private Logger logger = Decodex.logger; - HashMap recipeList; + private static int RECIPE_NAME_STRING_INDEX = 0; + + private final HashMap recipeList; /** * Creates a new RecipeManager with no recipes. @@ -21,6 +25,17 @@ public RecipeManager() { recipeList = new HashMap<>(); } + /** + * Returns a sorted list of recipe names. + * + * @return A sorted String array of recipe names. + */ + public String[] getRecipeNames() { + String[] recipeNames = recipeList.keySet().toArray(new String[RECIPE_NAME_STRING_INDEX]); + Arrays.sort(recipeNames); + return recipeNames; + } + /** * Adds a recipe to recipe manager to be managed. * diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 6043393dee..595a59dc4d 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -1,6 +1,7 @@ package decodex.ui; import decodex.ui.messages.RegularMessages; + import java.util.Scanner; /** @@ -66,9 +67,9 @@ public void printOutput(String output) { } /** - * Prints the list of available modules. + * Prints the list of supported modules. * - * @param moduleList String of the list of available modules. + * @param moduleList String of the list of supported modules. */ public void printModuleList(String moduleList) { assert moduleList != null : "Module list should not be null"; @@ -76,6 +77,20 @@ public void printModuleList(String moduleList) { System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } + /** + * Prints the list of available recipes. + * + * @param recipeList String of the list of available recipes. + */ + public void printRecipeList(String recipeList) { + assert recipeList != null : "Recipe list should not be null"; + if (!recipeList.isBlank()) { + System.out.println(RegularMessages.LIST_RECIPES + "\n" + recipeList); + } else { + System.out.println(RegularMessages.NO_RECIPES + "\n"); + } + } + /** * Prints a message with a success status icon. * diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 2a13f29ccb..afcd60f3ad 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -10,6 +10,8 @@ public class ErrorMessages { public static final String NO_DATA_FOUND = "No data found"; public static final String BASE64_DECODING_FAILED_MESSAGE = "Your data does not seem to be in Base64 format"; public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; + public static final String TOO_MANY_COMMAND_ARGUMENTS = "Too many command arguments"; + public static final String INVALID_LIST_CATEGORY = "Invalid list category"; public static final String INVALID_MODULE_PARAMETERS = "Invalid module parameters"; public static final String MISSING_MODULE_PARAMETERS = "Missing module parameters"; public static final String TOO_MANY_MODULE_PARAMETERS = "Too many module parameters"; diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 67b6f47ad1..251b3a1827 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -7,5 +7,7 @@ public class RegularMessages { + "============================================================"; public static final String GOODBYE = "Goodbye!"; public static final String LIST_MODULES = "Here are the list of supported modules:"; + public static final String LIST_RECIPES = "Here are the list of available recipes:"; + public static final String NO_RECIPES = "You do not have any recipes"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; } diff --git a/src/test/java/decodex/commands/ListCommandTest.java b/src/test/java/decodex/commands/ListCommandTest.java new file mode 100644 index 0000000000..c508314f3d --- /dev/null +++ b/src/test/java/decodex/commands/ListCommandTest.java @@ -0,0 +1,47 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +import org.junit.jupiter.api.Test; + +public class ListCommandTest { + + private static final DataManager dataManager = new DataManager(); + private static final ModuleManager moduleManager = new ModuleManager(); + private static final RecipeManager recipeManager = new RecipeManager(); + private final Ui ui = new Ui(); + + @Test + public void run_listNull_success() throws CommandException { + String listCategory = null; + ListCommand listCommand = new ListCommand(listCategory); + listCommand.run(dataManager, moduleManager, ui, recipeManager); + } + + @Test + public void run_listModules_success() throws CommandException { + String listCategory = "modules"; + ListCommand listCommand = new ListCommand(listCategory); + listCommand.run(dataManager, moduleManager, ui, recipeManager); + } + + @Test + public void run_listRecipes_success() throws CommandException { + String listCategory = "recipes"; + ListCommand listCommand = new ListCommand(listCategory); + listCommand.run(dataManager, moduleManager, ui, recipeManager); + } + + @Test + public void run_listUnknown_throwsCommandException() { + String listCategory = "unknown"; + ListCommand listCommand = new ListCommand(listCategory); + assertThrows(CommandException.class, () -> listCommand.run(dataManager, moduleManager, ui, recipeManager)); + } +} diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 1bb6fe70f4..81803012e8 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -6,11 +6,13 @@ import decodex.commands.ListCommand; import decodex.commands.ResetCommand; import decodex.commands.SelectCommand; +import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class ParserTest { @@ -18,7 +20,7 @@ class ParserTest { Parser parser = new Parser(); @Test - void getCommandType_stringWithNoSeparator_expectCommandTypeString() throws ParserException { + public void getCommandType_stringWithNoSeparator_expectCommandTypeString() throws ParserException { String testInput = "test"; String expectedOutput = "test"; @@ -28,7 +30,7 @@ void getCommandType_stringWithNoSeparator_expectCommandTypeString() throws Parse } @Test - void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() throws ParserException { + public void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() throws ParserException { String testInput = "test data"; String expectedOutput = "test"; @@ -38,7 +40,7 @@ void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() throws } @Test - void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() throws ParserException { + public void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() throws ParserException { String testInput = "test data"; String expectedOutput = "data"; @@ -50,7 +52,8 @@ void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() t /* The JUnit test methods below are for testing "normal" usage flows for the respective commands.*/ @Test - void parseCommand_userInputSpecifyingValidData_expectInputCommand() throws ParserException { + public void parseCommand_userInputSpecifyingValidData_expectInputCommand() throws ParserException, + CommandException { String userInput = "input dummyData"; Command command = parser.parseCommand(userInput); @@ -60,7 +63,7 @@ void parseCommand_userInputSpecifyingValidData_expectInputCommand() throws Parse @Test - void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserException { + public void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserException, CommandException { String userInput = "exit"; Command command = parser.parseCommand(userInput); @@ -69,7 +72,8 @@ void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserExcep } @Test - void parseCommand_userInputSpecifyingValidData_expectListCommand() throws ParserException { + public void parseCommand_userInputSpecifyingValidListBlank_expectListCommand() throws ParserException, + CommandException { String userInput = "list"; Command command = parser.parseCommand(userInput); @@ -78,7 +82,43 @@ void parseCommand_userInputSpecifyingValidData_expectListCommand() throws Parser } @Test - void parseCommand_userInputSpecifyingReset_expectResetCommand() throws ParserException { + public void parseCommand_userInputSpecifyingValidListModules_expectListCommand() throws ParserException, + CommandException { + String userInput = "list modules"; + + Command command = parser.parseCommand(userInput); + + assertTrue(command instanceof ListCommand); + } + + @Test + public void parseCommand_userInputSpecifyingValidListRecipes_expectListCommand() throws ParserException, + CommandException { + String userInput = "list recipes"; + + Command command = parser.parseCommand(userInput); + + assertTrue(command instanceof ListCommand); + } + + @Test + public void parseCommand_userInputSpecifyingValidListSpaces_expectListCommand() throws ParserException, + CommandException { + String userInput = "list modules "; + + Command command = parser.parseCommand(userInput); + + assertTrue(command instanceof ListCommand); + } + + @Test + public void parseCommand_userInputSpecifyingValidListAdditionalArguments_expectListCommand() { + String userInput = "list modules a"; + assertThrows(CommandException.class, () -> parser.parseCommand(userInput)); + } + + @Test + public void parseCommand_userInputSpecifyingReset_expectResetCommand() throws ParserException, CommandException { String userInput = "reset"; Command command = parser.parseCommand(userInput); @@ -87,7 +127,8 @@ void parseCommand_userInputSpecifyingReset_expectResetCommand() throws ParserExc } @Test - void parseCommand_userInputSpecifyingValidSelect_expectSelectCommand() throws ParserException { + public void parseCommand_userInputSpecifyingValidSelect_expectSelectCommand() throws ParserException, + CommandException { String userInput = "select dummyMod"; Command command = parser.parseCommand(userInput); From 46ab963f9562f24550eef813d546ef7902b61cbf Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 22:10:52 +0800 Subject: [PATCH 255/607] Refactor `UnknownModuleException` to `ModuleManagerException` * Rename exception class file * Move exception message into `ErrorMessages` --- src/main/java/decodex/Decodex.java | 4 ++-- src/main/java/decodex/commands/Command.java | 4 ++-- .../java/decodex/commands/SelectCommand.java | 4 ++-- .../exception/ModuleManagerException.java | 8 ++++++++ .../exception/UnknownModuleException.java | 19 ------------------- .../java/decodex/modules/ModuleManager.java | 8 ++++---- .../decodex/ui/messages/ErrorMessages.java | 1 + .../decodex/commands/SelectCommandTest.java | 8 ++++---- .../decodex/modules/ModuleManagerTest.java | 10 +++++----- 9 files changed, 28 insertions(+), 38 deletions(-) create mode 100644 src/main/java/decodex/data/exception/ModuleManagerException.java delete mode 100644 src/main/java/decodex/data/exception/UnknownModuleException.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index f91f17577e..88602c94c1 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -9,7 +9,7 @@ import decodex.data.exception.ParserException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.parser.Parser; import decodex.recipes.RecipeManager; @@ -66,7 +66,7 @@ public void run() { command = parser.parseCommand(userInput); assert command != null : "Command should not be null"; command.run(dataManager, moduleManager, ui, recipeManager); - } catch (ParserException | CommandException | UnknownModuleException + } catch (ParserException | CommandException | ModuleManagerException | DataManagerException | ModuleException | RecipeException | RecipeManagerException err) { ui.printError(err); logger.fine(err.getMessage()); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index 3af70455a0..daf4777487 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -6,7 +6,7 @@ import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.ui.Ui; @@ -20,6 +20,6 @@ public Command() { } public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, UnknownModuleException, DataManagerException, ModuleException, RecipeException, + throws CommandException, ModuleManagerException, DataManagerException, ModuleException, RecipeException, RecipeManagerException; } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index ed6406ecf8..a65f63b49b 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -7,7 +7,7 @@ import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; @@ -29,7 +29,7 @@ public SelectCommand(String moduleName, String[] parameters) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws UnknownModuleException, CommandException, DataManagerException, ModuleException, RecipeException, + throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, RecipeManagerException { if (moduleName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); diff --git a/src/main/java/decodex/data/exception/ModuleManagerException.java b/src/main/java/decodex/data/exception/ModuleManagerException.java new file mode 100644 index 0000000000..070492d470 --- /dev/null +++ b/src/main/java/decodex/data/exception/ModuleManagerException.java @@ -0,0 +1,8 @@ +package decodex.data.exception; + +public class ModuleManagerException extends Exception { + + public ModuleManagerException(String message) { + super(message); + } +} diff --git a/src/main/java/decodex/data/exception/UnknownModuleException.java b/src/main/java/decodex/data/exception/UnknownModuleException.java deleted file mode 100644 index 091717b699..0000000000 --- a/src/main/java/decodex/data/exception/UnknownModuleException.java +++ /dev/null @@ -1,19 +0,0 @@ -package decodex.data.exception; - -/** - * UnknownModuleException is thrown when an unknown module name is provided. - */ -public class UnknownModuleException extends Exception { - - public static final String EXCEPTION_MESSAGE = "Unknown module name"; - private final String unknownModuleName; - - public UnknownModuleException(String unknownModuleName) { - super(EXCEPTION_MESSAGE); - this.unknownModuleName = unknownModuleName; - } - - public String getUnknownModuleName() { - return unknownModuleName; - } -} diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index dfbd10a98a..d9889ec418 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -4,7 +4,7 @@ import decodex.Decodex; import decodex.data.exception.ModuleException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; import decodex.modules.binary.BinaryDecoder; @@ -54,10 +54,10 @@ public String[][] getModules() { * @param moduleName Name of selected module. * @param parameters Parameters for specific commands. * @return Selected module. - * @throws UnknownModuleException If provided module name is not an available module. + * @throws ModuleManagerException If provided module name is not an available module. * @throws ModuleException If module parameters are invalid. */ - public Module selectModule(String moduleName, String[] parameters) throws UnknownModuleException, ModuleException { + public Module selectModule(String moduleName, String[] parameters) throws ModuleManagerException, ModuleException { assert parameters != null : "parameters should not be null"; Module module; @@ -85,7 +85,7 @@ public Module selectModule(String moduleName, String[] parameters) throws Unknow module = prepareRotEncoder(parameters); break; default: - throw new UnknownModuleException(moduleName); + throw new ModuleManagerException(ErrorMessages.UNKNOWN_MODULE_NAME); } logger.fine(String.format("Selected %s", module.getName())); diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 38c4e710a8..c4148e71b8 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -15,6 +15,7 @@ public class ErrorMessages { public static final String DUPLICATE_RECIPE_NAME_MESSAGE = "A recipe with the given name already exists."; public static final String RECIPE_NOT_FOUND_MESSAGE = "The given recipe could not be found."; public static final String EDITING_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected for editing."; + public static final String UNKNOWN_MODULE_NAME = "Unknown module name"; public static final String INVALID_MODULE_PARAMETERS = "Invalid module parameters"; public static final String MISSING_MODULE_PARAMETERS = "Missing module parameters"; public static final String TOO_MANY_MODULE_PARAMETERS = "Too many module parameters"; diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index 80f29347a0..00e046ca12 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -10,7 +10,7 @@ import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.ui.Ui; @@ -21,7 +21,7 @@ public class SelectCommandTest { @Test public void run_validModuleName_success() - throws UnknownModuleException, CommandException, DataManagerException, ModuleException, RecipeException, + throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); @@ -41,7 +41,7 @@ public void run_validModuleName_success() @Test public void run_moduleWithParameters_success() - throws UnknownModuleException, CommandException, ModuleException, DataManagerException, RecipeException, + throws ModuleManagerException, CommandException, ModuleException, DataManagerException, RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); @@ -90,7 +90,7 @@ public void run_unknownModuleName_expectException() { String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(moduleName, parameters); - assertThrows(UnknownModuleException.class, () -> selectCommand.run(dataManager, moduleManager, ui, + assertThrows(ModuleManagerException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); } } diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index d0aa61c5fc..2b37dd4923 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import decodex.data.exception.ModuleException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.base64.Base64Encoder; import decodex.modules.rot.RotEncoder; @@ -13,7 +13,7 @@ public class ModuleManagerTest { @Test - public void selectModule_base64encode_success() throws UnknownModuleException, ModuleException { + public void selectModule_base64encode_success() throws ModuleManagerException, ModuleException { String moduleName = "base64encode"; String[] parameters = {}; ModuleManager moduleManager = new ModuleManager(); @@ -23,7 +23,7 @@ public void selectModule_base64encode_success() throws UnknownModuleException, M } @Test - public void selectModule_rotEncodeWithValidParameter_success() throws UnknownModuleException, ModuleException { + public void selectModule_rotEncodeWithValidParameter_success() throws ModuleManagerException, ModuleException { String moduleName = "rotencode"; String[] parameters = {"13"}; ModuleManager moduleManager = new ModuleManager(); @@ -37,7 +37,7 @@ public void selectModule_unknownModuleName_unknownModuleExceptionThrown() { String moduleName = "unknownModule"; String[] parameters = {}; ModuleManager moduleManager = new ModuleManager(); - assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); + assertThrows(ModuleManagerException.class, () -> moduleManager.selectModule(moduleName, parameters)); } @Test @@ -45,7 +45,7 @@ public void selectModule_blankModuleName_unknownModuleExceptionThrown() { String moduleName = ""; String[] parameters = {}; ModuleManager moduleManager = new ModuleManager(); - assertThrows(UnknownModuleException.class, () -> moduleManager.selectModule(moduleName, parameters)); + assertThrows(ModuleManagerException.class, () -> moduleManager.selectModule(moduleName, parameters)); } @Test From e870601b6f366e240563b3663ce9b237971a56dc Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 21 Oct 2021 22:43:41 +0800 Subject: [PATCH 256/607] Update `ListCommand` * Move stripping of list category to `Parser` * Simplify `ListCommand`'s `run()` method * Simplify `RecipeManager`'s `getRecipeNames()` method --- .../java/decodex/commands/ListCommand.java | 18 ++++++++--------- src/main/java/decodex/parser/Parser.java | 20 ++++++++++++++----- .../java/decodex/recipes/RecipeManager.java | 11 ++++++---- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 7015443ae6..e4ca0390d0 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -19,24 +19,22 @@ public class ListCommand extends Command { public ListCommand(String listCategory) { super(); - if (listCategory != null) { - listCategory = listCategory.strip(); - } this.listCategory = listCategory; } @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) throws CommandException { - if (listCategory == null) { - // Print both module and recipe list - ui.printModuleList(getModuleList(moduleManager)); - ui.printRecipeList(getRecipeList(recipeManager)); - } else if (listCategory.equals(LIST_CATEGORY_MODULES)) { + boolean isPrintModuleList = listCategory == null || listCategory.equals(LIST_CATEGORY_MODULES); + boolean isPrintRecipeList = listCategory == null || listCategory.equals(LIST_CATEGORY_RECIPE); + + if (isPrintModuleList) { ui.printModuleList(getModuleList(moduleManager)); - } else if (listCategory.equals(LIST_CATEGORY_RECIPE)) { + } + if (isPrintRecipeList) { ui.printRecipeList(getRecipeList(recipeManager)); - } else { + } + if (!isPrintModuleList && !isPrintRecipeList) { throw new CommandException(ErrorMessages.INVALID_LIST_CATEGORY); } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 916b5ac904..dcdfdcbb2a 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -43,6 +43,13 @@ public class Parser { */ private static final int VALID_TOKENS_LENGTH_FOR_COMMAND = 1; + /** + * Specifies the number of arguments for list command with no category and with a category. + */ + private static final int LIST_ARGUMENT_LENGTH_NO_CATEGORY = 0; + private static final int LIST_ARGUMENT_LENGTH_HAS_CATEGORY = 1; + private static final int LIST_ARGUMENT_STARTING_INDEX = 0; + /** * Returns the type of command that the user has specified. * @@ -184,13 +191,13 @@ private InputCommand craftInputCommand(String userInput) throws ParserException * @throws CommandException If the command has more than 1 argument. */ private String getListCategory(String userInput) throws CommandException { - String[] arguments = userInput.split(SPLIT_REGEX); - assert arguments.length > 0 : "userInput should have command word"; + String[] tokens = userInput.split(SPLIT_REGEX); + String[] arguments = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); - if (arguments.length == 1) { + if (arguments.length == LIST_ARGUMENT_LENGTH_NO_CATEGORY) { return null; - } else if (arguments.length == 2) { - return arguments[STARTING_ARGUMENTS_INDEX]; + } else if (arguments.length == LIST_ARGUMENT_LENGTH_HAS_CATEGORY) { + return arguments[LIST_ARGUMENT_STARTING_INDEX]; } else { throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); } @@ -204,6 +211,9 @@ private String getListCategory(String userInput) throws CommandException { */ private ListCommand craftListCommand(String userInput) throws CommandException { String listCategory = getListCategory(userInput); + if (listCategory != null) { + listCategory = listCategory.strip(); + } return new ListCommand(listCategory); } diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index b252923f00..4812be19ad 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.HashMap; +import java.util.Set; import java.util.logging.Logger; import decodex.Decodex; @@ -16,7 +17,7 @@ public class RecipeManager { private Logger logger = Decodex.logger; - + private static int RECIPE_NAME_STRING_INDEX = 0; /** @@ -43,9 +44,10 @@ public RecipeManager() { * @return A sorted String array of recipe names. */ public String[] getRecipeNames() { - String[] recipeNames = recipeList.keySet().toArray(new String[RECIPE_NAME_STRING_INDEX]); - Arrays.sort(recipeNames); - return recipeNames; + Set recipeNameSet = recipeList.keySet(); + String[] recipeNameArray = recipeNameSet.toArray(new String[RECIPE_NAME_STRING_INDEX]); + Arrays.sort(recipeNameArray); + return recipeNameArray; } /** @@ -108,6 +110,7 @@ public Recipe getEditingRecipe() throws RecipeManagerException { /** * Sets the recipe of the given name to be the recipe currently being edited. + * * @param name The name of the recipe to select. * @throws RecipeManagerException If the recipe could not be found in the recipe manager. */ From f2fd3863b257bd2518857709c8e15d5baccd68ad Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 22 Oct 2021 18:33:23 +0800 Subject: [PATCH 257/607] Changes to the error messages and some of the suggested changes. --- src/main/java/decodex/storage/Storage.java | 17 +++++++++-------- .../java/decodex/ui/messages/ErrorMessages.java | 8 ++------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 6b0a6400bf..a4ef306861 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -10,6 +10,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +// @@author authorName Kair0s3 public class Storage { /** @@ -29,14 +30,15 @@ public Storage() throws IOException { instantiateDirectories(); } + // @@author authorName Kair0s3 /** - * Reads the contents from the default input file. + * Reads the contents from the provided input file. * * @param fileName The name of the file specified by the user. * @return The byte contents from the default input file. * @throws IOException If the default input file does not exist. */ - public byte[] readFromDefaultInputFile(String fileName) throws IOException { + public byte[] readFromInputFile(String fileName) throws IOException { byte[] inputContent; File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); File inputFile = new File(inputDirectory, fileName); @@ -45,11 +47,12 @@ public byte[] readFromDefaultInputFile(String fileName) throws IOException { try { inputContent = Files.readAllBytes(inputFilePath); } catch (IOException e) { - throw new IOException(ErrorMessages.DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE); + throw new IOException(ErrorMessages.INPUT_FILE_DOES_NOT_EXIST_MESSAGE); } return inputContent; } + // @@author authorName Kair0s3 /** * Writes the given output into a file. * @@ -68,11 +71,6 @@ public void writeOutputToFile(String output) throws IOException { try { outputFile.createNewFile(); - } catch (IOException e) { - throw new IOException(ErrorMessages.FILE_CREATION_ERROR_MESSAGE); - } - - try { FileWriter fw = new FileWriter(outputFile); fw.write(output + "\n"); fw.close(); @@ -81,6 +79,7 @@ public void writeOutputToFile(String output) throws IOException { } } + // @@author authorName Kair0s3 /** * Instantiates the necessary directories if they do not exist yet. */ @@ -89,6 +88,7 @@ private void instantiateDirectories() { instantiateOutputDirectory(); } + // @@author authorName Kair0s3 /** * Instantiates the default input directory. */ @@ -99,6 +99,7 @@ private void instantiateInputDirectory() { } } + // @@author authorName Kair0s3 /** * Instantiates the default output directory. */ diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 65f2f0e080..568d552223 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -10,11 +10,7 @@ public class ErrorMessages { public static final String NO_DATA_FOUND = "No data found"; public static final String BASE64_DECODING_FAILED_MESSAGE = "Your data does not seem to be in Base64 format"; public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; - public static final String DEFAULT_INPUT_FILE_DOES_NOT_EXIST_MESSAGE = + public static final String INPUT_FILE_DOES_NOT_EXIST_MESSAGE = "The default input file does not exists"; - public static final String DEFAULT_INPUT_FILE_CREATION_ERROR_MESSAGE = - "The default input file failed to be created"; - public static final String FILE_CREATION_ERROR_MESSAGE = - "Something went wrong while reading from default input file"; - public static final String FILE_WRITE_ERROR_MESSAGE = "Something went wrong while writing to output file"; + public static final String FILE_WRITE_ERROR_MESSAGE = "Something went wrong while writing result to output file"; } From 6a33dc06d0a9bc3598fc30f26e66169358611bc8 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Fri, 22 Oct 2021 20:01:52 +0800 Subject: [PATCH 258/607] Add run method to Recipe - Add run method to Recipe - Add JUnit tests for Recipe run method --- src/main/java/decodex/recipes/Recipe.java | 27 +++++++++++++++++ src/test/java/decodex/recipes/RecipeTest.java | 29 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index 138f169650..1bee3c8f34 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -1,5 +1,10 @@ package decodex.recipes; +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; +import decodex.ui.Ui; import java.util.ArrayList; import java.util.logging.Logger; @@ -67,6 +72,28 @@ public Module pop() throws RecipeException { return removedModule; } + /** + * Runs the current recipe + * + * @param dataManager The dataManager containing the data to be manipulated. + * @param ui The Ui object handling any ui operations. + * @throws RecipeException If there are no modules in the recipe. + * @throws ModuleException If the selected module cannot be found. + * @throws DataManagerException If there is no data to be found. + */ + public void run(DataManager dataManager, Ui ui) throws RecipeException, ModuleException, DataManagerException { + if (moduleList.isEmpty()) { + throw new RecipeException(ErrorMessages.EMPTY_RECIPE_MESSAGE); + } + + for (Module module : moduleList) { + Data newData = module.run(dataManager.getCurrentData()); + dataManager.setCurrentData(newData); + + ui.printOutput(newData.toString()); + } + } + /** * Completely removes all the modules from the recipe. */ diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index 8f93eba45a..d616f09efd 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -4,12 +4,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; +import decodex.modules.binary.BinaryEncoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; import decodex.modules.Module; +import decodex.modules.rot.RotEncoder; +import decodex.ui.Ui; import org.junit.jupiter.api.Test; class RecipeTest { @@ -54,4 +61,26 @@ void reset_emptyModuleList() { assertTrue(recipe.getModuleList().isEmpty()); } + @Test + void run_chainOfModules_encodedOutput() throws RecipeException, ModuleException, DataManagerException { + Recipe recipe = new Recipe("rainbowTest"); + DataManager dataManager = new DataManager(); + Ui ui = new Ui(); + + Data inputData = new Data("egg"); + dataManager.setOriginalData(inputData); + + recipe.push(new HexEncoder()); + recipe.push(new Base64Encoder()); + recipe.push(new RotEncoder(5)); + recipe.push(new BinaryEncoder()); + + recipe.run(dataManager, ui); + + String result = dataManager.getCurrentData().toString(); + String expectedOutput = "0101001101101111010110100011001001010011011001010100010000110011"; + + assertEquals(result, expectedOutput); + } + } From 0ec48ce50bc549642b80757ecc4fc736c6cb55a8 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Fri, 22 Oct 2021 20:04:06 +0800 Subject: [PATCH 259/607] Resolve style errors --- src/main/java/decodex/recipes/Recipe.java | 2 +- src/test/java/decodex/recipes/RecipeTest.java | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index 1bee3c8f34..c0e396baea 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -73,7 +73,7 @@ public Module pop() throws RecipeException { } /** - * Runs the current recipe + * Runs the current recipe. * * @param dataManager The dataManager containing the data to be manipulated. * @param ui The Ui object handling any ui operations. diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index d616f09efd..67088a1d39 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -64,17 +64,18 @@ void reset_emptyModuleList() { @Test void run_chainOfModules_encodedOutput() throws RecipeException, ModuleException, DataManagerException { Recipe recipe = new Recipe("rainbowTest"); - DataManager dataManager = new DataManager(); - Ui ui = new Ui(); - - Data inputData = new Data("egg"); - dataManager.setOriginalData(inputData); recipe.push(new HexEncoder()); recipe.push(new Base64Encoder()); recipe.push(new RotEncoder(5)); recipe.push(new BinaryEncoder()); + DataManager dataManager = new DataManager(); + Data inputData = new Data("egg"); + dataManager.setOriginalData(inputData); + + Ui ui = new Ui(); + recipe.run(dataManager, ui); String result = dataManager.getCurrentData().toString(); From 306dd0a5427ac1be7d6c25a788349af23138a6d6 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Fri, 22 Oct 2021 20:23:28 +0800 Subject: [PATCH 260/607] Add hyperlinks to DG --- docs/DeveloperGuide.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index da3b4288c8..4544e7203a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,32 +2,32 @@ ## Table of Contents -- Acknowledgements -- Setting Up, Getting Started - - Setting Up +- [Acknowledgements](#acknowledgements) +- [Setting Up, Getting Started](#setting-up-getting-started) + - [Setting Up](#setting-up-the-on-your-computer) - Forking and cloning repository - Editor - Configure JDK - Importing project - Verifying setup - - Before writing code + - [Before writing code](#before-writing-code) - Configure the coding style - Set up CI - About our code structure -- Design - - Architecture - - UI Component - - Logic Component - - Data Component - - Module Component - - Recipe Component - - Storage Component -- Documentation, Logging, Testing, Configuration, Dev-Ops -- Appendix A: Product Scope -- Appendix B: User Stories -- Appendix C: Non Functional Requirements -- Appendix D: Glossary -- Appendix E: Instructions for Manual Testing (more details below) +- [Design](#design) + - [Architecture](#architecture) + - [UI Component](#ui-component) + - [Logic Component](#logic-component) + - [Data Component](#data-component) + - [Module Component](#module-component) + - [Recipe Component](#recipe-component) + - [Storage Component](#storage-component) +- [Documentation, Logging, Testing, Configuration, Dev-Ops](#documentation-logging-testing-configuration-and-dev-ops) +- [Appendix A: Product Scope](#appendix-a-product-scope) +- [Appendix B: User Stories](#appendix-b-user-stories) +- [Appendix C: Non Functional Requirements](#appendix-c-non-functional-requirements) +- [Appendix D: Glossary](#appendix-d-glossary) +- [Appendix E: Instructions for Manual Testing (more details below)](#appendix-e-instructions-for-manual-testing) ## Acknowledgements From 84477aa4bd0cd93e65bd825ff88a269cbf6dadc6 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 22 Oct 2021 20:28:44 +0800 Subject: [PATCH 261/607] Add RecipeSelectCommand --- .../decodex/commands/RecipeSelectCommand.java | 32 ++++++++++ .../java/decodex/recipes/RecipeManager.java | 1 + src/main/java/decodex/ui/Ui.java | 6 ++ .../decodex/ui/messages/ErrorMessages.java | 1 + .../decodex/ui/messages/RegularMessages.java | 1 + .../commands/RecipeSelectCommandTest.java | 60 +++++++++++++++++++ 6 files changed, 101 insertions(+) create mode 100644 src/main/java/decodex/commands/RecipeSelectCommand.java create mode 100644 src/test/java/decodex/commands/RecipeSelectCommandTest.java diff --git a/src/main/java/decodex/commands/RecipeSelectCommand.java b/src/main/java/decodex/commands/RecipeSelectCommand.java new file mode 100644 index 0000000000..d07af2af09 --- /dev/null +++ b/src/main/java/decodex/commands/RecipeSelectCommand.java @@ -0,0 +1,32 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; + +public class RecipeSelectCommand extends Command { + + public static final String COMMAND_WORD = "select"; + + private final String recipeName; + + public RecipeSelectCommand(String recipeName) { + super(); + this.recipeName = recipeName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, RecipeManagerException { + if (recipeName.isBlank()) { + throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); + } + + recipeManager.selectRecipeForEditing(recipeName); + ui.printRecipeSelected(recipeName); + } +} diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 8f2d91b335..3cfedcd557 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -102,6 +102,7 @@ public void selectRecipeForEditing(String name) throws RecipeManagerException { throw new RecipeManagerException(ErrorMessages.RECIPE_NOT_FOUND_MESSAGE); } editingRecipeName = name; + logger.fine(String.format("[RecipeManager] Selected recipe %s", name)); } /** diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 6043393dee..1f02823bf3 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -76,6 +76,12 @@ public void printModuleList(String moduleList) { System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } + public void printRecipeSelected(String recipeName) { + assert recipeName != null : "Module list should not be null"; + assert !recipeName.isBlank() : "Module list should not be empty"; + System.out.printf(RegularMessages.RECIPE_SELECT + "\n", recipeName); + } + /** * Prints a message with a success status icon. * diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 38c4e710a8..62db469b5e 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -5,6 +5,7 @@ public class ErrorMessages { public static final String MISSING_ARGUMENT = "Missing argument"; public static final String MISSING_COMMAND_TYPE = "Your command cannot be blank"; public static final String MISSING_MODULE_NAME = "Missing module name"; + public static final String MISSING_RECIPE_NAME = "Missing recipe name"; public static final String INPUT_EMPTY = "Your input is empty"; public static final String UNKNOWN_COMMAND = "Unknown command, please enter a valid command"; public static final String NO_DATA_FOUND = "No data found"; diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 67b6f47ad1..9a4bd5cc2a 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -8,4 +8,5 @@ public class RegularMessages { public static final String GOODBYE = "Goodbye!"; public static final String LIST_MODULES = "Here are the list of supported modules:"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; + public static final String RECIPE_SELECT = "Recipe %s selected for editing."; } diff --git a/src/test/java/decodex/commands/RecipeSelectCommandTest.java b/src/test/java/decodex/commands/RecipeSelectCommandTest.java new file mode 100644 index 0000000000..3bbac451d0 --- /dev/null +++ b/src/test/java/decodex/commands/RecipeSelectCommandTest.java @@ -0,0 +1,60 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +class RecipeSelectCommandTest { + + @Test + public void run_selectExistingRecipe_existingRecipe() throws RecipeManagerException, CommandException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertEquals(recipeManager.getEditingRecipe().getName(), testRecipeName); + } + + @Test + public void run_selectNonExistentRecipe_expectException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + + RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } + + @Test + public void run_nullRecipe_expectException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = ""; + + RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); + assertThrows(CommandException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } +} From 7f6a08a234503ec6848931c04ba5c401598bf143 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 22 Oct 2021 20:47:22 +0800 Subject: [PATCH 262/607] Add RecipePushCommand --- .../decodex/commands/RecipePushCommand.java | 38 ++++++++++++ src/main/java/decodex/ui/Ui.java | 14 +++++ .../decodex/ui/messages/RegularMessages.java | 1 + .../commands/RecipePushCommandTest.java | 61 +++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 src/main/java/decodex/commands/RecipePushCommand.java create mode 100644 src/test/java/decodex/commands/RecipePushCommandTest.java diff --git a/src/main/java/decodex/commands/RecipePushCommand.java b/src/main/java/decodex/commands/RecipePushCommand.java new file mode 100644 index 0000000000..2520f0eee2 --- /dev/null +++ b/src/main/java/decodex/commands/RecipePushCommand.java @@ -0,0 +1,38 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; + +public class RecipePushCommand extends Command { + + public static final String COMMAND_WORD = "push"; + + private final String moduleName; + private final String[] parameters; + + public RecipePushCommand(String moduleName, String[] parameters) { + super(); + this.moduleName = moduleName; + this.parameters = parameters; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, UnknownModuleException, ModuleException, RecipeManagerException { + if (moduleName.isBlank()) { + throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); + } + + Module module = moduleManager.selectModule(moduleName, parameters); + recipeManager.pushModuleIntoEditedRecipe(module); + ui.printModuleAddedToRecipe(module.getName(), recipeManager.getEditingRecipe().getName()); + } +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 6043393dee..2254fc21b1 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -76,6 +76,20 @@ public void printModuleList(String moduleList) { System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } + /** + * Prints a message showing that a given module was added to a given recipe. + * + * @param moduleName Name of module added. + * @param recipeName Name of recipe which the module was added to. + */ + public void printModuleAddedToRecipe(String moduleName, String recipeName) { + assert moduleName != null : "Module name should not be null"; + assert !moduleName.isBlank() : "Module name should not be empty"; + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + printSuccess(String.format(RegularMessages.ADDED_MODULE_TO_RECIPE + "\n", moduleName, recipeName)); + } + /** * Prints a message with a success status icon. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 67b6f47ad1..9ed2b9b293 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -8,4 +8,5 @@ public class RegularMessages { public static final String GOODBYE = "Goodbye!"; public static final String LIST_MODULES = "Here are the list of supported modules:"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; + public static final String ADDED_MODULE_TO_RECIPE = "Added %s module to recipe %s"; } diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java new file mode 100644 index 0000000000..650e07830e --- /dev/null +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -0,0 +1,61 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +class RecipePushCommandTest { + + @Test + public void run_oneModuleToEditingRecipe_recipeSizeIsOne() + throws RecipeManagerException, UnknownModuleException, CommandException, ModuleException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + String moduleName = "base64encode"; + String[] parameters = {}; + + RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 1); + } + + @Test + public void run_emptyModuleName_expectException() + throws RecipeManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + String moduleName = ""; + String[] parameters = {}; + + RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); + + assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } +} From 4413f27bd4124a8b5e0f4955cdd0aa5c14546892 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 22 Oct 2021 20:49:07 +0800 Subject: [PATCH 263/607] Add Javadocs and fix assert message --- src/main/java/decodex/ui/Ui.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 1f02823bf3..e3b63561ae 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -76,10 +76,15 @@ public void printModuleList(String moduleList) { System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } + /** + * Prints a message showing that a recipe was selected. + * + * @param recipeName Name of the selected recipe. + */ public void printRecipeSelected(String recipeName) { - assert recipeName != null : "Module list should not be null"; - assert !recipeName.isBlank() : "Module list should not be empty"; - System.out.printf(RegularMessages.RECIPE_SELECT + "\n", recipeName); + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + printSuccess(String.format(RegularMessages.RECIPE_SELECT + "\n", recipeName)); } /** From 932d9df75234a21e35b9352db306548fbdd03a48 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 22 Oct 2021 21:10:32 +0800 Subject: [PATCH 264/607] Add RecipePopCommand --- .../decodex/commands/RecipePopCommand.java | 25 ++++++++ src/main/java/decodex/ui/Ui.java | 14 +++++ .../decodex/ui/messages/RegularMessages.java | 1 + .../commands/RecipePopCommandTest.java | 61 +++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 src/main/java/decodex/commands/RecipePopCommand.java create mode 100644 src/test/java/decodex/commands/RecipePopCommandTest.java diff --git a/src/main/java/decodex/commands/RecipePopCommand.java b/src/main/java/decodex/commands/RecipePopCommand.java new file mode 100644 index 0000000000..48ec20d8c7 --- /dev/null +++ b/src/main/java/decodex/commands/RecipePopCommand.java @@ -0,0 +1,25 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +public class RecipePopCommand extends Command { + + public static final String COMMAND_WORD = "pop"; + + public RecipePopCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws RecipeException, RecipeManagerException { + Module module = recipeManager.popModuleFromEditedRecipe(); + ui.printModuleRemovedFromRecipe(module.getName(), recipeManager.getEditingRecipe().getName()); + } +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 6043393dee..1b3bedc9f6 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -76,6 +76,20 @@ public void printModuleList(String moduleList) { System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } + /** + * Prints a message showing that a given module was removed from a given recipe. + * + * @param moduleName Name of module removed. + * @param recipeName Name of recipe which the module was removed from. + */ + public void printModuleRemovedFromRecipe(String moduleName, String recipeName) { + assert moduleName != null : "Module name should not be null"; + assert !moduleName.isBlank() : "Module name should not be empty"; + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + printSuccess(String.format(RegularMessages.REMOVED_RECIPE_FROM_RECIPE + "\n", moduleName, recipeName)); + } + /** * Prints a message with a success status icon. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 67b6f47ad1..b8d65f062c 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -8,4 +8,5 @@ public class RegularMessages { public static final String GOODBYE = "Goodbye!"; public static final String LIST_MODULES = "Here are the list of supported modules:"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; + public static final String REMOVED_RECIPE_FROM_RECIPE = "Removed %s module from recipe %s"; } diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/commands/RecipePopCommandTest.java new file mode 100644 index 0000000000..5eb7cecde1 --- /dev/null +++ b/src/test/java/decodex/commands/RecipePopCommandTest.java @@ -0,0 +1,61 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.exception.RecipeException; +import org.junit.jupiter.api.Test; + +import decodex.data.DataManager; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.modules.Module; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +class RecipePopCommandTest { + + @Test + public void run_oneModuleToEditingRecipe_recipeSizeIsZero() + throws RecipeManagerException, UnknownModuleException, ModuleException, RecipeException { + + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + ModuleManager moduleManager = new ModuleManager(); + String moduleName = "base64encode"; + String[] parameters = {}; + Module module = moduleManager.selectModule(moduleName, parameters); + testRecipe.push(module); + + RecipePopCommand testCommand = new RecipePopCommand(); + DataManager dataManager = new DataManager(); + Ui ui = new Ui(); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); + } + + @Test + public void run_emptyEditingRecipe_expectException() throws RecipeManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + RecipePopCommand testCommand = new RecipePopCommand(); + + assertThrows(RecipeException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } +} From 556a2c048c3ed40ad38adce6800fb923818a30b3 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 22 Oct 2021 21:12:22 +0800 Subject: [PATCH 265/607] Fix method signature --- src/test/java/decodex/commands/RecipePushCommandTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java index 650e07830e..297c481daa 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -39,8 +39,7 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsOne() } @Test - public void run_emptyModuleName_expectException() - throws RecipeManagerException { + public void run_emptyModuleName_expectException() throws RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); From 06bdf768a4770e1a7097d735632bca315861c4b0 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 22 Oct 2021 21:30:42 +0800 Subject: [PATCH 266/607] Add RecipeResetCommand --- .../decodex/commands/RecipeResetCommand.java | 23 +++++++++++ src/main/java/decodex/ui/Ui.java | 6 +++ .../decodex/ui/messages/RegularMessages.java | 1 + .../commands/RecipeResetCommandTest.java | 40 +++++++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 src/main/java/decodex/commands/RecipeResetCommand.java create mode 100644 src/test/java/decodex/commands/RecipeResetCommandTest.java diff --git a/src/main/java/decodex/commands/RecipeResetCommand.java b/src/main/java/decodex/commands/RecipeResetCommand.java new file mode 100644 index 0000000000..80d5b6b1f8 --- /dev/null +++ b/src/main/java/decodex/commands/RecipeResetCommand.java @@ -0,0 +1,23 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +public class RecipeResetCommand extends Command { + + public static final String COMMAND_WORD = "reset"; + + public RecipeResetCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws RecipeManagerException { + recipeManager.resetEditedRecipe(); + ui.printRecipeReset(recipeManager.getEditingRecipe().getName()); + } +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 6043393dee..0022a9196d 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -76,6 +76,12 @@ public void printModuleList(String moduleList) { System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } + public void printRecipeReset(String recipeName) { + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + printSuccess(String.format(RegularMessages.RESETTED_EDITING_RECIPE, recipeName)); + } + /** * Prints a message with a success status icon. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 67b6f47ad1..990b8096c5 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -8,4 +8,5 @@ public class RegularMessages { public static final String GOODBYE = "Goodbye!"; public static final String LIST_MODULES = "Here are the list of supported modules:"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; + public static final String RESETTED_EDITING_RECIPE = "Removed all modules from recipe %s"; } diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/commands/RecipeResetCommandTest.java new file mode 100644 index 0000000000..e625b768ef --- /dev/null +++ b/src/test/java/decodex/commands/RecipeResetCommandTest.java @@ -0,0 +1,40 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +import decodex.data.DataManager; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.modules.Module; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +class RecipeResetCommandTest { + + @Test + public void run_oneModuleInEditingRecipe_recipeSizeIsZero() + throws RecipeManagerException, UnknownModuleException, ModuleException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + ModuleManager moduleManager = new ModuleManager(); + String moduleName = "base64encode"; + String[] parameters = {}; + Module module = moduleManager.selectModule(moduleName, parameters); + testRecipe.push(module); + + RecipeResetCommand testCommand = new RecipeResetCommand(); + DataManager dataManager = new DataManager(); + Ui ui = new Ui(); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); + } +} From 4f2e0ec0c406d9ef96acd9a2047f9c5892242be9 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 22 Oct 2021 21:31:40 +0800 Subject: [PATCH 267/607] Add Javadoc --- src/main/java/decodex/ui/Ui.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 0022a9196d..3f26c828b1 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -76,6 +76,11 @@ public void printModuleList(String moduleList) { System.out.println(RegularMessages.LIST_MODULES + "\n" + moduleList); } + /** + * Prints the message when a recipe is reset. + * + * @param recipeName Name of the recipe that was reset. + */ public void printRecipeReset(String recipeName) { assert recipeName != null : "Recipe name should not be null"; assert !recipeName.isBlank() : "Recipe name should not be empty"; From ac8163397ac837286b21a3543fd1df042f887ad8 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sat, 23 Oct 2021 00:43:05 +0800 Subject: [PATCH 268/607] Updates UI and tests * Update `UI` JavaDocs * Update `ListCommandTest` to assert successful execution * Remove blank lines in `ParserTest` --- src/main/java/decodex/ui/Ui.java | 4 +-- .../decodex/commands/ListCommandTest.java | 29 +++++++++++++++++-- src/test/java/decodex/parser/ParserTest.java | 25 +--------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 595a59dc4d..786ea7fd18 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -69,7 +69,7 @@ public void printOutput(String output) { /** * Prints the list of supported modules. * - * @param moduleList String of the list of supported modules. + * @param moduleList The list of supported modules. */ public void printModuleList(String moduleList) { assert moduleList != null : "Module list should not be null"; @@ -80,7 +80,7 @@ public void printModuleList(String moduleList) { /** * Prints the list of available recipes. * - * @param recipeList String of the list of available recipes. + * @param recipeList The list of available recipes. */ public void printRecipeList(String recipeList) { assert recipeList != null : "Recipe list should not be null"; diff --git a/src/test/java/decodex/commands/ListCommandTest.java b/src/test/java/decodex/commands/ListCommandTest.java index c508314f3d..b7f87572e2 100644 --- a/src/test/java/decodex/commands/ListCommandTest.java +++ b/src/test/java/decodex/commands/ListCommandTest.java @@ -1,27 +1,48 @@ package decodex.commands; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.ui.Ui; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ListCommandTest { - private static final DataManager dataManager = new DataManager(); - private static final ModuleManager moduleManager = new ModuleManager(); - private static final RecipeManager recipeManager = new RecipeManager(); + private ByteArrayOutputStream outputStream; + private final PrintStream originalOutputStream = System.out; + + private final DataManager dataManager = new DataManager(); + private final ModuleManager moduleManager = new ModuleManager(); + private final RecipeManager recipeManager = new RecipeManager(); private final Ui ui = new Ui(); + @BeforeEach + public void setOutputStream() { + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + } + + @AfterEach + public void restoreOutputStream() { + System.setOut(originalOutputStream); + } + @Test public void run_listNull_success() throws CommandException { String listCategory = null; ListCommand listCommand = new ListCommand(listCategory); listCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); } @Test @@ -29,6 +50,7 @@ public void run_listModules_success() throws CommandException { String listCategory = "modules"; ListCommand listCommand = new ListCommand(listCategory); listCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); } @Test @@ -36,6 +58,7 @@ public void run_listRecipes_success() throws CommandException { String listCategory = "recipes"; ListCommand listCommand = new ListCommand(listCategory); listCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); } @Test diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 81803012e8..bd269d02d8 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -1,8 +1,8 @@ package decodex.parser; import decodex.commands.Command; -import decodex.commands.InputCommand; import decodex.commands.ExitCommand; +import decodex.commands.InputCommand; import decodex.commands.ListCommand; import decodex.commands.ResetCommand; import decodex.commands.SelectCommand; @@ -10,7 +10,6 @@ import decodex.data.exception.ParserException; import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -23,9 +22,7 @@ class ParserTest { public void getCommandType_stringWithNoSeparator_expectCommandTypeString() throws ParserException { String testInput = "test"; String expectedOutput = "test"; - String commandType = parser.getCommandType(testInput); - assertEquals(expectedOutput, commandType); } @@ -33,9 +30,7 @@ public void getCommandType_stringWithNoSeparator_expectCommandTypeString() throw public void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() throws ParserException { String testInput = "test data"; String expectedOutput = "test"; - String commandType = parser.getCommandType(testInput); - assertEquals(expectedOutput, commandType); } @@ -43,9 +38,7 @@ public void getCommandType_stringWithOneSpaceSeparator_expectCommandTypeString() public void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentString() throws ParserException { String testInput = "test data"; String expectedOutput = "data"; - String arguments = parser.getInputString(testInput); - assertEquals(expectedOutput, arguments); } @@ -55,9 +48,7 @@ public void getUserArguments_userInputWithOneSpaceSeparator_expectOneArgumentStr public void parseCommand_userInputSpecifyingValidData_expectInputCommand() throws ParserException, CommandException { String userInput = "input dummyData"; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof InputCommand); } @@ -65,9 +56,7 @@ public void parseCommand_userInputSpecifyingValidData_expectInputCommand() throw @Test public void parseCommand_userInputSpecifyingExit_expectExitCommand() throws ParserException, CommandException { String userInput = "exit"; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof ExitCommand); } @@ -75,9 +64,7 @@ public void parseCommand_userInputSpecifyingExit_expectExitCommand() throws Pars public void parseCommand_userInputSpecifyingValidListBlank_expectListCommand() throws ParserException, CommandException { String userInput = "list"; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof ListCommand); } @@ -85,9 +72,7 @@ public void parseCommand_userInputSpecifyingValidListBlank_expectListCommand() t public void parseCommand_userInputSpecifyingValidListModules_expectListCommand() throws ParserException, CommandException { String userInput = "list modules"; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof ListCommand); } @@ -95,9 +80,7 @@ public void parseCommand_userInputSpecifyingValidListModules_expectListCommand() public void parseCommand_userInputSpecifyingValidListRecipes_expectListCommand() throws ParserException, CommandException { String userInput = "list recipes"; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof ListCommand); } @@ -105,9 +88,7 @@ public void parseCommand_userInputSpecifyingValidListRecipes_expectListCommand() public void parseCommand_userInputSpecifyingValidListSpaces_expectListCommand() throws ParserException, CommandException { String userInput = "list modules "; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof ListCommand); } @@ -120,9 +101,7 @@ public void parseCommand_userInputSpecifyingValidListAdditionalArguments_expectL @Test public void parseCommand_userInputSpecifyingReset_expectResetCommand() throws ParserException, CommandException { String userInput = "reset"; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof ResetCommand); } @@ -130,9 +109,7 @@ public void parseCommand_userInputSpecifyingReset_expectResetCommand() throws Pa public void parseCommand_userInputSpecifyingValidSelect_expectSelectCommand() throws ParserException, CommandException { String userInput = "select dummyMod"; - Command command = parser.parseCommand(userInput); - assertTrue(command instanceof SelectCommand); } } From 4311f7b8464ae66ba8eebb25e888de1eb45922ff Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:00:59 +0800 Subject: [PATCH 269/607] Add requested change --- src/main/java/decodex/ui/Ui.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 1b3bedc9f6..6583ae781a 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -79,8 +79,8 @@ public void printModuleList(String moduleList) { /** * Prints a message showing that a given module was removed from a given recipe. * - * @param moduleName Name of module removed. - * @param recipeName Name of recipe which the module was removed from. + * @param moduleName The name of the module removed. + * @param recipeName The name of the recipe which the module was removed from. */ public void printModuleRemovedFromRecipe(String moduleName, String recipeName) { assert moduleName != null : "Module name should not be null"; From 2cbb5fba2643174b6be4118b38275bba20792ea0 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:02:16 +0800 Subject: [PATCH 270/607] Add requested change --- src/main/java/decodex/commands/RecipePopCommand.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/decodex/commands/RecipePopCommand.java b/src/main/java/decodex/commands/RecipePopCommand.java index 48ec20d8c7..4f3c37bd65 100644 --- a/src/main/java/decodex/commands/RecipePopCommand.java +++ b/src/main/java/decodex/commands/RecipePopCommand.java @@ -5,6 +5,7 @@ import decodex.data.exception.RecipeManagerException; import decodex.modules.Module; import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; @@ -20,6 +21,7 @@ public RecipePopCommand() { public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) throws RecipeException, RecipeManagerException { Module module = recipeManager.popModuleFromEditedRecipe(); + Recipe editingRecipe = recipeManager.getEditingRecipe(); ui.printModuleRemovedFromRecipe(module.getName(), recipeManager.getEditingRecipe().getName()); } } From 1ba681d4e50e2257bf5b2a5501ba4384534d3138 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:03:57 +0800 Subject: [PATCH 271/607] Add requested changes --- src/main/java/decodex/commands/RecipePushCommand.java | 4 +++- src/main/java/decodex/ui/Ui.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/commands/RecipePushCommand.java b/src/main/java/decodex/commands/RecipePushCommand.java index 2520f0eee2..76328526cd 100644 --- a/src/main/java/decodex/commands/RecipePushCommand.java +++ b/src/main/java/decodex/commands/RecipePushCommand.java @@ -7,6 +7,7 @@ import decodex.data.exception.UnknownModuleException; import decodex.modules.Module; import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -32,7 +33,8 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec } Module module = moduleManager.selectModule(moduleName, parameters); + Recipe editingRecipe = recipeManager.getEditingRecipe(); recipeManager.pushModuleIntoEditedRecipe(module); - ui.printModuleAddedToRecipe(module.getName(), recipeManager.getEditingRecipe().getName()); + ui.printModuleAddedToRecipe(module.getName(), editingRecipe.getName()); } } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 2254fc21b1..8511621b6d 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -79,8 +79,8 @@ public void printModuleList(String moduleList) { /** * Prints a message showing that a given module was added to a given recipe. * - * @param moduleName Name of module added. - * @param recipeName Name of recipe which the module was added to. + * @param moduleName The name of the module added. + * @param recipeName The name of the recipe which the module was added to. */ public void printModuleAddedToRecipe(String moduleName, String recipeName) { assert moduleName != null : "Module name should not be null"; From bfb38820a98fbd587f961dfe0bdff33e35ed1cb7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:07:11 +0800 Subject: [PATCH 272/607] Fix reference --- src/main/java/decodex/commands/RecipePopCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/commands/RecipePopCommand.java b/src/main/java/decodex/commands/RecipePopCommand.java index 4f3c37bd65..f080404ff3 100644 --- a/src/main/java/decodex/commands/RecipePopCommand.java +++ b/src/main/java/decodex/commands/RecipePopCommand.java @@ -22,6 +22,6 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec throws RecipeException, RecipeManagerException { Module module = recipeManager.popModuleFromEditedRecipe(); Recipe editingRecipe = recipeManager.getEditingRecipe(); - ui.printModuleRemovedFromRecipe(module.getName(), recipeManager.getEditingRecipe().getName()); + ui.printModuleRemovedFromRecipe(module.getName(), editingRecipe.getName()); } } From 61bb809da786c0afdf8e30cfe075e047e33cd31a Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:11:06 +0800 Subject: [PATCH 273/607] Rename exception --- src/test/java/decodex/commands/RecipePopCommandTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/commands/RecipePopCommandTest.java index 5eb7cecde1..bf7e17abee 100644 --- a/src/test/java/decodex/commands/RecipePopCommandTest.java +++ b/src/test/java/decodex/commands/RecipePopCommandTest.java @@ -9,7 +9,7 @@ import decodex.data.DataManager; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.modules.Module; import decodex.recipes.Recipe; @@ -20,7 +20,7 @@ class RecipePopCommandTest { @Test public void run_oneModuleToEditingRecipe_recipeSizeIsZero() - throws RecipeManagerException, UnknownModuleException, ModuleException, RecipeException { + throws RecipeManagerException, ModuleManagerException, ModuleException, RecipeException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "test"; From 43b051ad2cc6565af06cf0f72bfb50c48f7e292b Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:12:33 +0800 Subject: [PATCH 274/607] Rename exception --- src/main/java/decodex/commands/RecipePushCommand.java | 4 ++-- src/test/java/decodex/commands/RecipePushCommandTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/commands/RecipePushCommand.java b/src/main/java/decodex/commands/RecipePushCommand.java index 76328526cd..4136a89a2c 100644 --- a/src/main/java/decodex/commands/RecipePushCommand.java +++ b/src/main/java/decodex/commands/RecipePushCommand.java @@ -4,7 +4,7 @@ import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; @@ -27,7 +27,7 @@ public RecipePushCommand(String moduleName, String[] parameters) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, UnknownModuleException, ModuleException, RecipeManagerException { + throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException { if (moduleName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); } diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java index 297c481daa..9ab593899d 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -8,7 +8,7 @@ import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; @@ -18,7 +18,7 @@ class RecipePushCommandTest { @Test public void run_oneModuleToEditingRecipe_recipeSizeIsOne() - throws RecipeManagerException, UnknownModuleException, CommandException, ModuleException { + throws RecipeManagerException, CommandException, ModuleException, ModuleManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); From 6422d96f88de3d10d11b7d8e87ce8d33d548ec88 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:15:06 +0800 Subject: [PATCH 275/607] Add requested changes --- src/main/java/decodex/commands/RecipeResetCommand.java | 4 +++- src/main/java/decodex/ui/Ui.java | 2 +- src/test/java/decodex/commands/RecipeResetCommandTest.java | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeResetCommand.java b/src/main/java/decodex/commands/RecipeResetCommand.java index 80d5b6b1f8..09f6426f02 100644 --- a/src/main/java/decodex/commands/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/RecipeResetCommand.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; @@ -17,7 +18,8 @@ public RecipeResetCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) throws RecipeManagerException { + Recipe editingRecipe = recipeManager.getEditingRecipe(); recipeManager.resetEditedRecipe(); - ui.printRecipeReset(recipeManager.getEditingRecipe().getName()); + ui.printRecipeReset(editingRecipe.getName()); } } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 3f26c828b1..81b3bacc9f 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -79,7 +79,7 @@ public void printModuleList(String moduleList) { /** * Prints the message when a recipe is reset. * - * @param recipeName Name of the recipe that was reset. + * @param recipeName The name of the recipe that was reset. */ public void printRecipeReset(String recipeName) { assert recipeName != null : "Recipe name should not be null"; diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/commands/RecipeResetCommandTest.java index e625b768ef..a99aa15df0 100644 --- a/src/test/java/decodex/commands/RecipeResetCommandTest.java +++ b/src/test/java/decodex/commands/RecipeResetCommandTest.java @@ -6,7 +6,7 @@ import decodex.data.DataManager; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.modules.Module; import decodex.recipes.Recipe; @@ -17,7 +17,7 @@ class RecipeResetCommandTest { @Test public void run_oneModuleInEditingRecipe_recipeSizeIsZero() - throws RecipeManagerException, UnknownModuleException, ModuleException { + throws RecipeManagerException, ModuleManagerException, ModuleException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "test"; Recipe testRecipe = new Recipe(testRecipeName); From 81e5ea96a50e28518e356b046a5aab3ed02d3af3 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 01:16:11 +0800 Subject: [PATCH 276/607] Add requested changes --- src/main/java/decodex/ui/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index e3b63561ae..51621b0fae 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -79,7 +79,7 @@ public void printModuleList(String moduleList) { /** * Prints a message showing that a recipe was selected. * - * @param recipeName Name of the selected recipe. + * @param recipeName The name of the selected recipe. */ public void printRecipeSelected(String recipeName) { assert recipeName != null : "Recipe name should not be null"; From 88d2af48be3e151cb48c2b42aa64dfc3fd438937 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 01:36:27 +0800 Subject: [PATCH 277/607] Add Recipe New Command --- .../decodex/commands/RecipeNewCommand.java | 37 +++++++++++++++++++ src/main/java/decodex/ui/Ui.java | 11 ++++++ .../decodex/ui/messages/ErrorMessages.java | 1 + .../decodex/ui/messages/RegularMessages.java | 1 + .../commands/RecipeNewCommandTest.java | 35 ++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 src/main/java/decodex/commands/RecipeNewCommand.java create mode 100644 src/test/java/decodex/commands/RecipeNewCommandTest.java diff --git a/src/main/java/decodex/commands/RecipeNewCommand.java b/src/main/java/decodex/commands/RecipeNewCommand.java new file mode 100644 index 0000000000..cc8ff89690 --- /dev/null +++ b/src/main/java/decodex/commands/RecipeNewCommand.java @@ -0,0 +1,37 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; + +public class RecipeNewCommand extends Command { + + public static final String COMMAND_WORD = "new"; + + private final String recipeName; + + public RecipeNewCommand(String recipeName) { + super(); + this.recipeName = recipeName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, UnknownModuleException, ModuleException, RecipeManagerException { + if (recipeName.isBlank()) { + throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); + } + + Recipe recipe = new Recipe(recipeName); + + recipeManager.addRecipe(recipe); + ui.printNewRecipeCreated(recipe.getName()); + } +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 6043393dee..e13eb1a2ae 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -98,6 +98,17 @@ public void printError(Exception exception) { System.out.println(ERROR_ICON + " " + exception.getMessage()); } + /** + * Prints a message indicating the creation of a new recipe. + * + * @param recipeName The name of the recipe created. + */ + public void printNewRecipeCreated(String recipeName) { + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + printSuccess(String.format(RegularMessages.NEW_RECIPE_CREATED + "\n", recipeName)); + } + /** * Prints the prompt header. */ diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 38c4e710a8..62db469b5e 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -5,6 +5,7 @@ public class ErrorMessages { public static final String MISSING_ARGUMENT = "Missing argument"; public static final String MISSING_COMMAND_TYPE = "Your command cannot be blank"; public static final String MISSING_MODULE_NAME = "Missing module name"; + public static final String MISSING_RECIPE_NAME = "Missing recipe name"; public static final String INPUT_EMPTY = "Your input is empty"; public static final String UNKNOWN_COMMAND = "Unknown command, please enter a valid command"; public static final String NO_DATA_FOUND = "No data found"; diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 67b6f47ad1..60cd354aa6 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -8,4 +8,5 @@ public class RegularMessages { public static final String GOODBYE = "Goodbye!"; public static final String LIST_MODULES = "Here are the list of supported modules:"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; + public static final String NEW_RECIPE_CREATED = "Recipe %s created successfully"; } diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/commands/RecipeNewCommandTest.java new file mode 100644 index 0000000000..e414d1ce70 --- /dev/null +++ b/src/test/java/decodex/commands/RecipeNewCommandTest.java @@ -0,0 +1,35 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import org.junit.jupiter.api.Test; + +class RecipeNewCommandTest { + + @Test + void run_createNewRecipe_newRecipeCreated() + throws UnknownModuleException, CommandException, RecipeException, + ModuleException, RecipeManagerException, DataManagerException { + String testRecipeName = "BaconPancakes"; + Command newRecipeCommand = new RecipeNewCommand(testRecipeName); + + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + RecipeManager recipeManager = new RecipeManager(); + + newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager); + + assertNotNull(recipeManager.getRecipe("BaconPancakes")); + } +} From 5a115ccbb8f21ac9b1b1b83f6b83a289776d951d Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 01:44:03 +0800 Subject: [PATCH 278/607] Minor edit minor edit to check github actions --- src/main/java/decodex/ui/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index e13eb1a2ae..b37d3d857c 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -99,7 +99,7 @@ public void printError(Exception exception) { } /** - * Prints a message indicating the creation of a new recipe. + * Prints a message indicating the creation of a new recipe of given name. * * @param recipeName The name of the recipe created. */ From 3aa9c61f97b05914584cf362665e00b1205dd611 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 02:05:39 +0800 Subject: [PATCH 279/607] Add recipe delete command --- .../decodex/commands/RecipeDeleteCommand.java | 34 ++++++++++++ src/main/java/decodex/ui/Ui.java | 11 ++++ .../decodex/ui/messages/ErrorMessages.java | 1 + .../decodex/ui/messages/RegularMessages.java | 1 + .../commands/RecipeDeleteCommandTest.java | 55 +++++++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 src/main/java/decodex/commands/RecipeDeleteCommand.java create mode 100644 src/test/java/decodex/commands/RecipeDeleteCommandTest.java diff --git a/src/main/java/decodex/commands/RecipeDeleteCommand.java b/src/main/java/decodex/commands/RecipeDeleteCommand.java new file mode 100644 index 0000000000..60faf31936 --- /dev/null +++ b/src/main/java/decodex/commands/RecipeDeleteCommand.java @@ -0,0 +1,34 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; + +public class RecipeDeleteCommand extends Command { + + public static final String COMMAND_WORD = "delete"; + + private final String recipeName; + + public RecipeDeleteCommand(String recipeName) { + super(); + this.recipeName = recipeName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, UnknownModuleException, ModuleException, RecipeManagerException { + if (recipeName.isBlank()) { + throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); + } + + recipeManager.removeRecipe(recipeName); + ui.printRecipeDeleted(recipeName); + } +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 6043393dee..74b27af510 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -98,6 +98,17 @@ public void printError(Exception exception) { System.out.println(ERROR_ICON + " " + exception.getMessage()); } + /** + * Prints a message indicating successful deletion of a recipe. + * + * @param recipeName The name of the recipe that was deleted. + */ + public void printRecipeDeleted(String recipeName) { + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + printSuccess(String.format(RegularMessages.RECIPE_DELETED + "\n", recipeName)); + } + /** * Prints the prompt header. */ diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 38c4e710a8..62db469b5e 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -5,6 +5,7 @@ public class ErrorMessages { public static final String MISSING_ARGUMENT = "Missing argument"; public static final String MISSING_COMMAND_TYPE = "Your command cannot be blank"; public static final String MISSING_MODULE_NAME = "Missing module name"; + public static final String MISSING_RECIPE_NAME = "Missing recipe name"; public static final String INPUT_EMPTY = "Your input is empty"; public static final String UNKNOWN_COMMAND = "Unknown command, please enter a valid command"; public static final String NO_DATA_FOUND = "No data found"; diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 67b6f47ad1..34b887ebe7 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -8,4 +8,5 @@ public class RegularMessages { public static final String GOODBYE = "Goodbye!"; public static final String LIST_MODULES = "Here are the list of supported modules:"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; + public static final String RECIPE_DELETED = "Recipe %s has been deleted"; } diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java new file mode 100644 index 0000000000..a40ba8d088 --- /dev/null +++ b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java @@ -0,0 +1,55 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.UnknownModuleException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import org.junit.jupiter.api.Test; + +class RecipeDeleteCommandTest { + + @Test + void run_deleteValidRecipe_recipeDeleted() + throws UnknownModuleException, CommandException, RecipeException, + ModuleException, RecipeManagerException, DataManagerException { + String testRecipeName = "BaconPancakes"; + + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + RecipeManager recipeManager = new RecipeManager(); + + recipeManager.addRecipe(new Recipe(testRecipeName)); + + Command recipeDeleteCommand = new RecipeDeleteCommand(testRecipeName); + recipeDeleteCommand.run(dataManager, moduleManager, ui, recipeManager); + + assertThrows(RecipeManagerException.class, () -> recipeManager.getRecipe(testRecipeName)); + } + + @Test + void run_deleteInvalidRecipe_expectException() throws RecipeManagerException { + + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + RecipeManager recipeManager = new RecipeManager(); + + recipeManager.addRecipe(new Recipe("BaconPancakes")); + + String testRecipeName = "IDoNotExist"; + Command recipeDeleteCommand = new RecipeDeleteCommand(testRecipeName); + + assertThrows(RecipeManagerException.class, + () -> recipeDeleteCommand.run(dataManager, moduleManager, ui, recipeManager)); + } +} From 4e053a9e1439896d4e97d8e08e41d63f591011a2 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 02:15:14 +0800 Subject: [PATCH 280/607] Add negative test case --- .../decodex/commands/RecipeNewCommandTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/commands/RecipeNewCommandTest.java index e414d1ce70..177ded476a 100644 --- a/src/test/java/decodex/commands/RecipeNewCommandTest.java +++ b/src/test/java/decodex/commands/RecipeNewCommandTest.java @@ -1,6 +1,7 @@ package decodex.commands; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import decodex.data.DataManager; import decodex.data.exception.CommandException; @@ -32,4 +33,18 @@ void run_createNewRecipe_newRecipeCreated() assertNotNull(recipeManager.getRecipe("BaconPancakes")); } + + @Test + void run_createNewRecipeWithNoName_expectException() { + String testRecipeName = ""; + Command newRecipeCommand = new RecipeNewCommand(testRecipeName); + + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + RecipeManager recipeManager = new RecipeManager(); + + assertThrows(CommandException.class, + () -> newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager)); + } } From 125d47b98c3f62f4e014ee4655c78fb12f66ba15 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 11:59:04 +0800 Subject: [PATCH 281/607] Fixed the unnecessary "authorName" placeholder and changed the instantiation of directories to be more dynamic/flexible. Will remove the remaining unused variables when I change the output file portion. --- src/main/java/decodex/storage/Storage.java | 40 ++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index a4ef306861..7ce98c8af2 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -10,9 +10,17 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -// @@author authorName Kair0s3 +// @@author Kair0s3 public class Storage { + /** + * List of default directories used. + * 1. input folder - for reading user input from file. + * 2. output folder - for writing processed data into file. + * 3. recipe folder - for reading and writing recipe from and to file. + */ + private final String [] DEFAULT_DIRECTORY_LIST = {"input", "output", "recipe"}; + /** * Variables for loading from the default input filepath. */ @@ -30,7 +38,7 @@ public Storage() throws IOException { instantiateDirectories(); } - // @@author authorName Kair0s3 + // @@author Kair0s3 /** * Reads the contents from the provided input file. * @@ -52,7 +60,7 @@ public byte[] readFromInputFile(String fileName) throws IOException { return inputContent; } - // @@author authorName Kair0s3 + // @@author Kair0s3 /** * Writes the given output into a file. * @@ -79,32 +87,22 @@ public void writeOutputToFile(String output) throws IOException { } } - // @@author authorName Kair0s3 + // @@author Kair0s3 /** - * Instantiates the necessary directories if they do not exist yet. + * Instantiates the default directories if they do not exist yet. */ private void instantiateDirectories() { - instantiateInputDirectory(); - instantiateOutputDirectory(); - } - - // @@author authorName Kair0s3 - /** - * Instantiates the default input directory. - */ - private void instantiateInputDirectory() { - File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); - if (!inputDirectory.exists()) { - inputDirectory.mkdir(); + for (String directoryName : DEFAULT_DIRECTORY_LIST) { + instantiateDirectory(directoryName); } } - // @@author authorName Kair0s3 + // @@author Kair0s3 /** - * Instantiates the default output directory. + * Instantiates the given directory. */ - private void instantiateOutputDirectory() { - File outputDirectory = new File(DEFAULT_OUTPUT_DIRECTORY); + private void instantiateDirectory(String directoryName) { + File outputDirectory = new File(directoryName); if (!outputDirectory.exists()) { outputDirectory.mkdir(); } From dd38ff6d1824c44af790b8b809814ad51b574d8b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 12:03:10 +0800 Subject: [PATCH 282/607] Added suggested changes for the exceptions and limiting the scope for inputContent --- src/main/java/decodex/storage/Storage.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 7ce98c8af2..26535bdbde 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -3,6 +3,7 @@ import decodex.ui.messages.ErrorMessages; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; @@ -47,17 +48,20 @@ public Storage() throws IOException { * @throws IOException If the default input file does not exist. */ public byte[] readFromInputFile(String fileName) throws IOException { - byte[] inputContent; + File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); File inputFile = new File(inputDirectory, fileName); Path inputFilePath = inputFile.toPath(); try { + byte[] inputContent; inputContent = Files.readAllBytes(inputFilePath); - } catch (IOException e) { - throw new IOException(ErrorMessages.INPUT_FILE_DOES_NOT_EXIST_MESSAGE); + return inputContent; + } catch (FileNotFoundException fileNotFoundError) { + throw new FileNotFoundException(ErrorMessages.INPUT_FILE_DOES_NOT_EXIST_MESSAGE); + } catch (IOException ioException) { + throw new IOException("Error during file read"); } - return inputContent; } // @@author Kair0s3 From 3d0f00f74d440e0a28a75058beccaa739891da84 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 12:23:00 +0800 Subject: [PATCH 283/607] rEMOVED UNNECESSARY VARIABLES --- src/main/java/decodex/storage/Storage.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 26535bdbde..52fedec6c8 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -23,14 +23,11 @@ public class Storage { private final String [] DEFAULT_DIRECTORY_LIST = {"input", "output", "recipe"}; /** - * Variables for loading from the default input filepath. + * The indexes of the corresponding directory in the list. */ - public static final String DEFAULT_INPUT_DIRECTORY = "input"; - - /** - * Variable for the default output directory. - */ - public static final String DEFAULT_OUTPUT_DIRECTORY = "output"; + private final int INPUT_DIRECTORY_INDEX = 0; + private final int OUTPUT_DIRECTORY_INDEX = 1; + private final int RECIPE_DIRECTORY_INDEX = 2; /** * Initializes a new Storage. @@ -48,8 +45,7 @@ public Storage() throws IOException { * @throws IOException If the default input file does not exist. */ public byte[] readFromInputFile(String fileName) throws IOException { - - File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); + File inputDirectory = new File(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); File inputFile = new File(inputDirectory, fileName); Path inputFilePath = inputFile.toPath(); @@ -78,7 +74,7 @@ public void writeOutputToFile(String output) throws IOException { String formattedDateTime = currentDateTime.format(dateTimeFormatter); String newOutputFileName = formattedDateTime + ".txt"; - File outputDirectory = new File(DEFAULT_OUTPUT_DIRECTORY); + File outputDirectory = new File(DEFAULT_DIRECTORY_LIST[OUTPUT_DIRECTORY_INDEX]); File outputFile = new File(outputDirectory, newOutputFileName); try { From ac2b8dd54ad6919f1903d77b3034d6b4ef44103e Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 13:10:25 +0800 Subject: [PATCH 284/607] Change of the access modifier --- src/main/java/decodex/storage/Storage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 52fedec6c8..be2bca4799 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -44,7 +44,7 @@ public Storage() throws IOException { * @return The byte contents from the default input file. * @throws IOException If the default input file does not exist. */ - public byte[] readFromInputFile(String fileName) throws IOException { + private byte[] readFromInputFile(String fileName) throws IOException { File inputDirectory = new File(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); File inputFile = new File(inputDirectory, fileName); Path inputFilePath = inputFile.toPath(); @@ -68,7 +68,7 @@ public byte[] readFromInputFile(String fileName) throws IOException { * @throws IOException If an I/O exception is caught when creating the output file * or when writing to the output file. */ - public void writeOutputToFile(String output) throws IOException { + private void writeOutputToFile(String output) throws IOException { LocalDateTime currentDateTime = LocalDateTime.now(); DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-dd-MM__HH.mm.ss"); String formattedDateTime = currentDateTime.format(dateTimeFormatter); From 8df49220aef75da54bf90dde4456d550eb02125e Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 15:23:44 +0800 Subject: [PATCH 285/607] Add current diagrams --- docs/puml/Style.puml | 16 ++++ .../classdiagrams/CommandClassDiagram.puml | 38 ++++++++++ docs/puml/classdiagrams/DataClassDiagram.puml | 30 ++++++++ .../classdiagrams/ModuleClassDiagram.puml | 49 ++++++++++++ .../ModuleManagerClassDiagram.puml | 31 ++++++++ .../classdiagrams/ParserClassDiagram.puml | 21 ++++++ .../DecodeRunSequenceDiagram.puml | 51 +++++++++++++ .../DecodexInitSequenceDiagram.puml | 75 +++++++++++++++++++ .../ParserSequenceDiagram.puml | 37 +++++++++ .../commands/ExitCommandSequenceDiagram.puml | 18 +++++ .../commands/InputCommandSequenceDiagram.puml | 32 ++++++++ .../commands/ResetCommandSequenceDiagram.puml | 23 ++++++ .../SelectCommandSequenceDiagram.puml | 45 +++++++++++ 13 files changed, 466 insertions(+) create mode 100644 docs/puml/Style.puml create mode 100644 docs/puml/classdiagrams/CommandClassDiagram.puml create mode 100644 docs/puml/classdiagrams/DataClassDiagram.puml create mode 100644 docs/puml/classdiagrams/ModuleClassDiagram.puml create mode 100644 docs/puml/classdiagrams/ModuleManagerClassDiagram.puml create mode 100644 docs/puml/classdiagrams/ParserClassDiagram.puml create mode 100644 docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/ParserSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml diff --git a/docs/puml/Style.puml b/docs/puml/Style.puml new file mode 100644 index 0000000000..a31bdb2722 --- /dev/null +++ b/docs/puml/Style.puml @@ -0,0 +1,16 @@ +!define LOGIC_COLOR #3333C4 +!define LOGIC_COLOR_T1 #7777DB +!define LOGIC_COLOR_T2 #5252CE +!define LOGIC_COLOR_T3 #1616B0 +!define LOGIC_COLOR_T4 #101086 + +skinparam Sequence { + MessageAlign center +} + +hide footbox +hide members +hide circle + +skinparam DefaultTextAlignment center +skinparam Shadowing false \ No newline at end of file diff --git a/docs/puml/classdiagrams/CommandClassDiagram.puml b/docs/puml/classdiagrams/CommandClassDiagram.puml new file mode 100644 index 0000000000..a00a58294a --- /dev/null +++ b/docs/puml/classdiagrams/CommandClassDiagram.puml @@ -0,0 +1,38 @@ +@startuml +skinparam classAttributeIconSize 0 +hide circle + +abstract class "Command\n{abstract}" as Command { + {abstract} +run(dataManager: DataManager, moduleManager: ModuleManager, ui: Ui, recipeManager: RecipeManager): void +} + +Command <|-- ExitCommand +Command <|-- InputCommand +Command <|-- ListCommand +Command <|-- ResetCommand +Command <|-- SelectCommand +Command <|-- HelpCommand + +class ExitCommand { +} + +note "run() method omitted \nfrom inheriting classes" as omitMessage + +class InputCommand { + - dataString: String +} + +class ListCommand { +} + +class ResetCommand { +} + +class SelectCommand { + - moduleName: String +} + +class HelpCommand { +} + +@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/DataClassDiagram.puml b/docs/puml/classdiagrams/DataClassDiagram.puml new file mode 100644 index 0000000000..16db42cb23 --- /dev/null +++ b/docs/puml/classdiagrams/DataClassDiagram.puml @@ -0,0 +1,30 @@ +@startuml +skinparam classAttributeIconSize 0 +hide circle + +Decodex -> "1" DataManager + +DataManager ---> "0..1 \nOriginal" Data +DataManager ---> "0..1 \nCurrent" Data + + +class Decodex { + - initDecodex(): void + + run(): void + + {static} main(): void +} + +class DataManager { + + getOriginalData(): Data + + getCurrentData(): Data + + setOriginalData(originalData: Data): Data + + setCurrentData(currentData: Data): Data + + resetToOriginalData(): void +} + +class Data { + - rawBytes: byte[] + + getRawBytes(): byte[] +} + +@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/ModuleClassDiagram.puml b/docs/puml/classdiagrams/ModuleClassDiagram.puml new file mode 100644 index 0000000000..b8add9c95e --- /dev/null +++ b/docs/puml/classdiagrams/ModuleClassDiagram.puml @@ -0,0 +1,49 @@ +@startuml +skinparam classAttributeIconSize 0 +hide circle + +abstract class "Module\n{abstract}" as Module { + # name: String + # description: String + {abstract} +run(data: Data): Data +} + +Module <|-right- Base64Decoder +Module <|-right- Base64Encoder +Module <|-left- HexDecoder +Module <|-left- HexEncoder +Module <|-down- BinaryEncoder +Module <|-down- BinaryDecoder +Module <|-up- RotEncoder + +class Base64Encoder { + +run(data: Data): Data +} + +class Base64Decoder { + +run(data: Data): Data +} + +class HexEncoder { + +run(data: Data): Data +} + +class HexDecoder { + +run(data: Data): Data +} + +class BinaryEncoder { + +run(data: Data): Data +} + +class BinaryDecoder { + +run(data: Data): Data +} + +class RotEncoder { + - rotateOffset: int + +run(data: Data): Data +} + + +@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml b/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml new file mode 100644 index 0000000000..c3f548530d --- /dev/null +++ b/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml @@ -0,0 +1,31 @@ +@startuml +skinparam classAttributeIconSize 0 +hide circle + +Decodex -> "1" ModuleManager + +class Decodex { + - initDecodex(): void + + run(): void + + {static} main(): void +} + + +class ModuleManager { + - {static} MODULE_LIST: String[][] + + getModules(): String[][] + + selectModule(moduleName: String, parameters: String[]): Module + - hasNoParameters(parameters: String): void + - prepareXYZEncoder(parameters: String[]): Module + - prepareXYZDecoder(parameters: String[]): Module +} + +note right + XYZEncoder = HexEncoder, + Base64Encoder, etc + + XYZDecoder = HexDecoder, + Base64Decoder, etc +end note + +@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/ParserClassDiagram.puml b/docs/puml/classdiagrams/ParserClassDiagram.puml new file mode 100644 index 0000000000..24a64165ac --- /dev/null +++ b/docs/puml/classdiagrams/ParserClassDiagram.puml @@ -0,0 +1,21 @@ +@startuml +skinparam classAttributeIconSize 0 +hide circle + +Decodex -> "1" Parser + + +class Decodex { + - initDecodex(): void + + run(): void + + {static} main(): void +} + +class Parser { + + getCommandType(userInput: String): String + + getModuleName(userInput: String): String + + getInputString(userInput: String): String + + parseCommand(userInput: String): Command +} + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml b/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml new file mode 100644 index 0000000000..c475264bc6 --- /dev/null +++ b/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml @@ -0,0 +1,51 @@ +@startuml +!include ../Style.puml + +Participant ":Decodex" as Decodex +Participant ":Ui" as Ui +Participant ":Parser" as Parser +Participant ":XYZCommand" as XYZCommand + +-> Decodex: run() + +activate Decodex + +Decodex -> Ui : printGreeting() +activate Ui +Ui --> Decodex +deactivate Ui + +loop command is not instance of ExitCommand + Decodex -> Ui : readInput() + activate Ui + Ui --> Decodex : userInput : String + deactivate Ui + + Decodex -> Parser : parseCommand(userInput :String) + activate Parser + + ref over Parser, XYZCommand : Handle selection of Command + + Parser --> Decodex : XYZCommand + deactivate Parser + + Decodex -> XYZCommand : run(:DataManager, :ModuleManager, :RecipeManager, :Ui) + activate XYZCommand + + ref over XYZCommand : Command execution + + XYZCommand --> Decodex + deactivate XYZCommand + XYZCommand -[hidden]-> Decodex + destroy XYZCommand + +end + +<-- Decodex : +deactivate Decodex +<-[hidden]- Decodex : + +destroy Ui +destroy Parser +destroy Decodex +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml b/docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml new file mode 100644 index 0000000000..f679fe9b1c --- /dev/null +++ b/docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include ../Style.puml +Participant "<>\nDecodex" as DecodexClass +Participant ":Decodex" as Decodex +Participant ":DataManager" as DataManager +Participant ":ModuleManager" as ModuleManager +Participant ":RecipeManager" as RecipeManager +Participant ":Parser" as Parser +Participant ":Storage" as Storage +Participant ":Ui" as Ui + +-> DecodexClass: main() +activate DecodexClass + +create Decodex +DecodexClass -> Decodex: new Decodex() +activate Decodex + +Decodex -> Decodex: initDecodex() +activate Decodex + +create DataManager + Decodex -> DataManager : new DataManager() + activate DataManager + Decodex <-- DataManager + deactivate DataManager + +create ModuleManager + Decodex -> ModuleManager : new ModuleManager() + activate ModuleManager + Decodex <-- ModuleManager + deactivate ModuleManager + +create RecipeManager + Decodex -> RecipeManager : new RecipeManager() + activate RecipeManager + Decodex <-- RecipeManager + deactivate RecipeManager + +create Parser + Decodex -> Parser : new Parser() + activate Parser + Decodex <-- Parser + deactivate Parser + +create Storage + Decodex -> Storage : new Storage() + activate Storage + Decodex <-- Storage + deactivate Storage + +create Ui + Decodex -> Ui : new Ui() + activate Ui + Decodex <-- Ui + deactivate Ui + +Decodex --> Decodex +deactivate Decodex + +DecodexClass <-- Decodex +deactivate Decodex + +'DecodexClass -> Decodex: run() +'activate Decodex +'DecodexClass <-- Decodex +'deactivate Decodex +'<-- DecodexClass +'deactivate DecodexClass +'Decodex -[hidden]> DecodexClass +'destroy Decodex + +DecodexClass <-[hidden]- Decodex +DecodexClass <-[hidden]- Decodex +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/ParserSequenceDiagram.puml b/docs/puml/sequencediagrams/ParserSequenceDiagram.puml new file mode 100644 index 0000000000..12b18bd4ee --- /dev/null +++ b/docs/puml/sequencediagrams/ParserSequenceDiagram.puml @@ -0,0 +1,37 @@ +@startuml +!include ../Style.puml + +Participant ":Parser" as Parser +Participant ":XYZCommand" as XYZCommand + +activate Parser +-> Parser : parseCommand(userInput : String) +Parser -> Parser : getCommandType(userInput : String) +activate Parser +Parser --> Parser : commandType +deactivate Parser +alt commandType is "xyz" + Parser -> Parser : prepareXYZCommand() + note right + XYZCommand = InputCommand, + SelectCommand, + ListCommand, etc + end note + activate Parser + create XYZCommand + Parser -> XYZCommand : new XYZCommand() + activate XYZCommand + note left + Arguments for prepareXYZCommand() + and XYZCommand() are omitted. + end note + XYZCommand --> Parser : XYZCommand + deactivate XYZCommand + Parser --> Parser : XYZCommand + deactivate Parser + <-- Parser : XYZCommand +else commandType is not valid + <-- Parser : ParserException +end + +@enduml diff --git a/docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml new file mode 100644 index 0000000000..ef00e61b93 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml @@ -0,0 +1,18 @@ +@startuml +!include ../../Style.puml + +Participant ":ExitCommand" as ExitCommand +Participant ":Ui" as Ui + +-> ExitCommand: run() +activate ExitCommand +ExitCommand -> Ui: printGoodbye() +activate Ui +ExitCommand <-- Ui +deactivate Ui +<-- ExitCommand + +deactivate ExitCommand + + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml new file mode 100644 index 0000000000..92a735bf46 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml @@ -0,0 +1,32 @@ +@startuml +!include ../../Style.puml + +Participant ":InputCommand" as InputCommand +Participant ":Data" as Data +Participant ":DataManager" as DataManager +Participant ":Ui" as Ui + +-> InputCommand: run() +activate InputCommand + +alt dataString is not empty + create Data + InputCommand -> Data : new Data(data: String) + activate Data + InputCommand <-- Data: userData:Data + deactivate Data + InputCommand -> DataManager: setOriginalData(originalData: Data) + activate DataManager + InputCommand <-- DataManager + deactivate DataManager + InputCommand -> Ui: printInput(input: String) + activate Ui + InputCommand <-- Ui + deactivate Ui + <-- InputCommand +else dataString is empty + <-- InputCommand: CommandException + deactivate InputCommand +end + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml new file mode 100644 index 0000000000..43caa07181 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml @@ -0,0 +1,23 @@ +@startuml +!include ../../Style.puml + +Participant ":ResetCommand" as ResetCommand +Participant ":DataManager" as DataManager +Participant ":Ui" as Ui + +-> ResetCommand: run() + +activate ResetCommand +ResetCommand -> DataManager: resetToOriginalData() +activate DataManager +ResetCommand <-- DataManager +deactivate DataManager + +ResetCommand -> Ui: printSuccess() +activate Ui +ResetCommand <-- Ui +deactivate Ui + +<-- ResetCommand + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml new file mode 100644 index 0000000000..9e53df1bd5 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml @@ -0,0 +1,45 @@ +@startuml +!include ../../Style.puml + +Participant ":SelectCommand" as SelectCommand +Participant ":ModuleManager" as ModuleManager +Participant ":DataManager" as DataManager +Participant ":Module" as Module +Participant ":Ui" as Ui + +-> SelectCommand: run() +activate SelectCommand + +alt moduleName is not blank + SelectCommand -> ModuleManager: selectModule(moduleName: String, parameters: String[]) + activate ModuleManager + SelectCommand <-- ModuleManager: module: Module + deactivate ModuleManager + + SelectCommand -> DataManager: getCurrentData() + activate DataManager + SelectCommand <-- DataManager: :Data + deactivate DataManager + + SelectCommand -> Module: run(data: Data) + activate Module + SelectCommand <-- Module: newData: Data + deactivate Module + + SelectCommand -> DataManager: setCurrentData(currentData: Data) + activate DataManager + SelectCommand <-- DataManager + deactivate DataManager + + SelectCommand -> Ui: printOutput(newData.toString()) + activate Ui + SelectCommand <-- Ui + deactivate Ui + + <-- SelectCommand +else moduleName is blank + <-- SelectCommand: CommandException + deactivate SelectCommand +end + +@enduml \ No newline at end of file From 61eaff75892f3f88e1725e9a9083f602cf639445 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 18:08:30 +0800 Subject: [PATCH 286/607] Remove toString() call from diagram --- .../sequencediagrams/commands/SelectCommandSequenceDiagram.puml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml index 9e53df1bd5..a09bfeb272 100644 --- a/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml @@ -31,7 +31,7 @@ alt moduleName is not blank SelectCommand <-- DataManager deactivate DataManager - SelectCommand -> Ui: printOutput(newData.toString()) + SelectCommand -> Ui: printOutput(newData) activate Ui SelectCommand <-- Ui deactivate Ui From b65c06c4da858cde366a02235c4c558ab544d9ce Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 18:12:08 +0800 Subject: [PATCH 287/607] Removed the instantiation from the Storage constructor. --- src/main/java/decodex/storage/Storage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index be2bca4799..b869ae08ee 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -32,11 +32,11 @@ public class Storage { /** * Initializes a new Storage. */ - public Storage() throws IOException { - instantiateDirectories(); + public Storage() { } // @@author Kair0s3 + /** * Reads the contents from the provided input file. * From 357259584df69188ee0c9d68b2684f79b31e33f3 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 18:50:02 +0800 Subject: [PATCH 288/607] Changed codes to read from recipe and input file. Also abstracted the common reading code logic into a seperate method call. Will be doing the output portion next. --- src/main/java/decodex/storage/Storage.java | 67 ++++++++++++++++------ 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index b869ae08ee..c7e4c58acb 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -36,22 +36,59 @@ public Storage() { } // @@author Kair0s3 - /** - * Reads the contents from the provided input file. + * Reads and returns the input from the provided file. * * @param fileName The name of the file specified by the user. * @return The byte contents from the default input file. - * @throws IOException If the default input file does not exist. + * @throws IOException If something went wrong when reading the file or + * the input file does not exist. */ - private byte[] readFromInputFile(String fileName) throws IOException { + private byte[] readInputFromFile(String fileName) throws IOException { + instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); + File inputDirectory = new File(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); File inputFile = new File(inputDirectory, fileName); Path inputFilePath = inputFile.toPath(); + byte[] inputContent = readContent(inputFilePath); + return inputContent; + } + + // @@author Kair0s3 + /** + * Reads and returns the recipe from the provided file. + * + * @param fileName The name of the file specified by the user. + * @return The string formatted recipe from the provided file. + * @throws IOException If something went wrong when reading the file or + * the input file does not exist. + */ + private String readRecipeFromFile(String fileName) throws IOException { + instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + + File recipeDirectory = new File(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + File recipeFile = new File(recipeDirectory, fileName); + Path recipeFilePath = recipeFile.toPath(); + + byte[] recipeContentBytes = readContent(recipeFilePath); + String recipeContent = recipeContentBytes.toString(); + return recipeContent; + } + + // @@author Kair0s3 + /** + * Reads and returns the contents. + * + * @param readFilePath The Path object of the file to be read. + * @return The bytes of the read content. + * @throws IOException If something went wrong when reading the file. + * @throws FileNotFoundException If the input file does not exist. + */ + private byte[] readContent(Path readFilePath) throws IOException, FileNotFoundException { try { byte[] inputContent; - inputContent = Files.readAllBytes(inputFilePath); + inputContent = Files.readAllBytes(readFilePath); return inputContent; } catch (FileNotFoundException fileNotFoundError) { throw new FileNotFoundException(ErrorMessages.INPUT_FILE_DOES_NOT_EXIST_MESSAGE); @@ -88,23 +125,19 @@ private void writeOutputToFile(String output) throws IOException { } // @@author Kair0s3 - /** - * Instantiates the default directories if they do not exist yet. - */ - private void instantiateDirectories() { - for (String directoryName : DEFAULT_DIRECTORY_LIST) { - instantiateDirectory(directoryName); - } - } - // @@author Kair0s3 /** - * Instantiates the given directory. + * Instantiates the given directory if it does not exist yet. */ - private void instantiateDirectory(String directoryName) { + private void instantiateDirectoryIfNotExist(String directoryName) throws IOException { File outputDirectory = new File(directoryName); + boolean isSuccessful = false; if (!outputDirectory.exists()) { - outputDirectory.mkdir(); + isSuccessful = outputDirectory.mkdir(); + } + + if (!isSuccessful) { + throw new IOException("Failed to create the directory for " + directoryName); } } } From 2624fc6e11e0eefc8e3f615fbe233ada542aa3d0 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 19:32:14 +0800 Subject: [PATCH 289/607] Add carbon outputs --- docs/carbon/inputcommand.txt | 4 ++++ docs/carbon/listcommand.txt | 14 ++++++++++++++ docs/carbon/product.txt | 10 ++++++++++ docs/carbon/recipedeletecommand.txt | 4 ++++ docs/carbon/recipenewcommand.txt | 5 +++++ docs/carbon/recipepopcommand.txt | 4 ++++ docs/carbon/recipepushcommand.txt | 5 +++++ docs/carbon/reciperesetcommand.txt | 4 ++++ docs/carbon/recipeselectcommand.txt | 5 +++++ docs/carbon/selectcommand.txt | 7 +++++++ 10 files changed, 62 insertions(+) create mode 100644 docs/carbon/inputcommand.txt create mode 100644 docs/carbon/listcommand.txt create mode 100644 docs/carbon/product.txt create mode 100644 docs/carbon/recipedeletecommand.txt create mode 100644 docs/carbon/recipenewcommand.txt create mode 100644 docs/carbon/recipepopcommand.txt create mode 100644 docs/carbon/recipepushcommand.txt create mode 100644 docs/carbon/reciperesetcommand.txt create mode 100644 docs/carbon/recipeselectcommand.txt create mode 100644 docs/carbon/selectcommand.txt diff --git a/docs/carbon/inputcommand.txt b/docs/carbon/inputcommand.txt new file mode 100644 index 0000000000..2c84280be5 --- /dev/null +++ b/docs/carbon/inputcommand.txt @@ -0,0 +1,4 @@ +Decodex [] > input SGVsbG9Xb3JsZA== +[+] Input: "SGVsbG9Xb3JsZA==" + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520input%2520SGVsbG9Xb3JsZA%253D%253D%250A%255B%252B%255D%2520Input%253A%2520%2522SGVsbG9Xb3JsZA%253D%253D%2522 \ No newline at end of file diff --git a/docs/carbon/listcommand.txt b/docs/carbon/listcommand.txt new file mode 100644 index 0000000000..c317faa0fd --- /dev/null +++ b/docs/carbon/listcommand.txt @@ -0,0 +1,14 @@ +Decodex [] > list +Here are the list of supported modules: + base64encode - Encodes the data using Base64 format. + base64decode - Decodes the data using Base64 format. + hexencode - Converts the input string to hexadecimal bytes + hexdecode - Converts a hexadecimal string back into its raw value + binencode - Encodes the data into binary. + bindecode - Decodes the data from binary. + rotencode - Rotates the alphabets in the input string by a specified amount +Here are the list of available recipes: + testRecipe1 + testRecipe2 + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520list%250AHere%2520are%2520the%2520list%2520of%2520supported%2520modules%253A%250A%2520%2520base64encode%2520-%2520Encodes%2520the%2520data%2520using%2520Base64%2520format.%250A%2520%2520base64decode%2520-%2520Decodes%2520the%2520data%2520using%2520Base64%2520format.%250A%2520%2520hexencode%2520%2520%2520%2520-%2520Converts%2520the%2520input%2520string%2520to%2520hexadecimal%2520bytes%250A%2520%2520hexdecode%2520%2520%2520%2520-%2520Converts%2520a%2520hexadecimal%2520string%2520back%2520into%2520its%2520raw%2520value%250A%2520%2520binencode%2520%2520%2520%2520-%2520Encodes%2520the%2520data%2520into%2520binary.%250A%2520%2520bindecode%2520%2520%2520%2520-%2520Decodes%2520the%2520data%2520from%2520binary.%250A%2520%2520rotencode%2520%2520%2520%2520-%2520Rotates%2520the%2520alphabets%2520in%2520the%2520input%2520string%2520by%2520a%2520specified%2520amount%250AHere%2520are%2520the%2520list%2520of%2520available%2520recipes%253A%250A%2520%2520testRecipe1%250A%2520%2520testRecipe2 diff --git a/docs/carbon/product.txt b/docs/carbon/product.txt new file mode 100644 index 0000000000..a8d86dbaac --- /dev/null +++ b/docs/carbon/product.txt @@ -0,0 +1,10 @@ +Decodex [] > input NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk= +[+] Input: "NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=" +Decodex [] > select base64decode +[+] Output: "5131524765304e544d6a45784d315239" +Decodex [] > select hexdecode +[+] Output: "Q1RGe0NTMjExM1R9" +Decodex [] > select base64decode +[+] Output: "CTF{CS2113T}" + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520input%2520NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk%253D%250A%255B%252B%255D%2520Input%253A%2520%2522NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk%253D%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520base64decode%250A%255B%252B%255D%2520Output%253A%2520%25225131524765304e544d6a45784d315239%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520hexdecode%250A%255B%252B%255D%2520Output%253A%2520%2522Q1RGe0NTMjExM1R9%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520base64decode%250A%255B%252B%255D%2520Output%253A%2520%2522CTF%257BCS2113T%257D%2522 \ No newline at end of file diff --git a/docs/carbon/recipedeletecommand.txt b/docs/carbon/recipedeletecommand.txt new file mode 100644 index 0000000000..723b41fd29 --- /dev/null +++ b/docs/carbon/recipedeletecommand.txt @@ -0,0 +1,4 @@ +Decodex [] > recipe delete testRecipe +[+] Recipe testRecipe has been deleted + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520recipe%2520delete%2520testRecipe%250A%255B%252B%255D%2520Recipe%2520testRecipe%2520has%2520been%2520deleted \ No newline at end of file diff --git a/docs/carbon/recipenewcommand.txt b/docs/carbon/recipenewcommand.txt new file mode 100644 index 0000000000..9d0d864811 --- /dev/null +++ b/docs/carbon/recipenewcommand.txt @@ -0,0 +1,5 @@ +Decodex [] > recipe new testRecipe +[+] Recipe testRecipe created successfully +Decodex [Editing: testRecipe] > + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520recipe%2520new%2520testRecipe%250A%255B%252B%255D%2520Recipe%2520testRecipe%2520created%2520successfully%250ADecodex%2520%255BEditing%253A%2520testRecipe%255D%2520%253E \ No newline at end of file diff --git a/docs/carbon/recipepopcommand.txt b/docs/carbon/recipepopcommand.txt new file mode 100644 index 0000000000..397382017d --- /dev/null +++ b/docs/carbon/recipepopcommand.txt @@ -0,0 +1,4 @@ +Decodex [Editing: testRecipe] > recipe pop +[+] Removed base64encode module from recipe testRecipe + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255BEditing%253A%2520testRecipe%255D%2520%253E%2520recipe%2520pop%250A%255B%252B%255D%2520Removed%2520base64encode%2520module%2520from%2520recipe%2520testRecipe \ No newline at end of file diff --git a/docs/carbon/recipepushcommand.txt b/docs/carbon/recipepushcommand.txt new file mode 100644 index 0000000000..eab11b2f94 --- /dev/null +++ b/docs/carbon/recipepushcommand.txt @@ -0,0 +1,5 @@ +Decodex [Editing: testRecipe] > recipe push base64encode +[+] Added base64encode module to recipe testRecipe + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255BEditing%253A%2520testRecipe%255D%2520%253E%2520recipe%2520push%2520base64encode%250A%255B%252B%255D%2520Added%2520base64encode%2520module%2520to%2520recipe%2520testRecipe + diff --git a/docs/carbon/reciperesetcommand.txt b/docs/carbon/reciperesetcommand.txt new file mode 100644 index 0000000000..e92f94d09a --- /dev/null +++ b/docs/carbon/reciperesetcommand.txt @@ -0,0 +1,4 @@ +Decodex [Editing: testRecipe] > recipe reset +[+] Removed all modules from recipe testRecipe + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255BEditing%253A%2520testRecipe%255D%2520%253E%2520recipe%2520pop%250A%255B%252B%255D%2520Removed%2520base64encode%2520module%2520from%2520recipe%2520testRecipe \ No newline at end of file diff --git a/docs/carbon/recipeselectcommand.txt b/docs/carbon/recipeselectcommand.txt new file mode 100644 index 0000000000..bf3c47fc78 --- /dev/null +++ b/docs/carbon/recipeselectcommand.txt @@ -0,0 +1,5 @@ +Decodex [] > recipe select testRecipe +[+] Recipe testRecipe selected for editing. +Decodex [Editing: testRecipe] > + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520recipe%2520select%2520testRecipe%250A%255B%252B%255D%2520Recipe%2520testRecipe%2520selected%2520for%2520editing.%250ADecodex%2520%255BEditing%253A%2520testRecipe%255D%2520%253E \ No newline at end of file diff --git a/docs/carbon/selectcommand.txt b/docs/carbon/selectcommand.txt new file mode 100644 index 0000000000..8755f4ad90 --- /dev/null +++ b/docs/carbon/selectcommand.txt @@ -0,0 +1,7 @@ +Decodex [] > select module base64encode +[+] Input: "SGVsbG9Xb3JsZA==" +Decodex [] > select recipe testRecipe1 +[+] Module executed: hexencode, hexencode +[+] Input: "3533343735363733363234373339353836323333346137333561343133643364" + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520select%2520module%2520base64encode%250A%255B%252B%255D%2520Input%253A%2520%2522SGVsbG9Xb3JsZA%253D%253D%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520recipe%2520testRecipe1%250A%255B%252B%255D%2520Module%2520executed%253A%2520hexencode%252C%2520hexencode%250A%255B%252B%255D%2520Input%253A%2520%25223533343735363733363234373339353836323333346137333561343133643364%2522 \ No newline at end of file From 32ef6fe9b3f155e11b2f8a55e08841839ad987b6 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 19:49:33 +0800 Subject: [PATCH 290/607] Add boxes --- docs/puml/Style.puml | 13 ++++++++----- .../sequencediagrams/DecodeRunSequenceDiagram.puml | 7 +++++++ .../DecodexInitSequenceDiagram.puml | 14 ++++++++++++++ .../sequencediagrams/ParserSequenceDiagram.puml | 2 ++ .../commands/ExitCommandSequenceDiagram.puml | 4 ++++ .../commands/InputCommandSequenceDiagram.puml | 6 ++++++ .../commands/ResetCommandSequenceDiagram.puml | 6 ++++++ .../commands/SelectCommandSequenceDiagram.puml | 10 +++++++++- 8 files changed, 56 insertions(+), 6 deletions(-) diff --git a/docs/puml/Style.puml b/docs/puml/Style.puml index a31bdb2722..f368f1fb22 100644 --- a/docs/puml/Style.puml +++ b/docs/puml/Style.puml @@ -1,8 +1,11 @@ -!define LOGIC_COLOR #3333C4 -!define LOGIC_COLOR_T1 #7777DB -!define LOGIC_COLOR_T2 #5252CE -!define LOGIC_COLOR_T3 #1616B0 -!define LOGIC_COLOR_T4 #101086 +!define DECODEX_COLOR #99D5CA +!define UI_COLOR #FFC374 +!define LOGIC_COLOR #FF8F80 +!define DATA_COLOR #F9D2DE +!define RECIPE_COLOR #FFDF71 +!define MODULE_COLOR #C7E8AC +!define BLUE_COLOR #99D2F2 +!define STORAGE_COLOR #99D2F2 skinparam Sequence { MessageAlign center diff --git a/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml b/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml index c475264bc6..6d2100e23e 100644 --- a/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml @@ -1,10 +1,17 @@ @startuml !include ../Style.puml +box DECODEX_COLOR Participant ":Decodex" as Decodex +end box +box UI_COLOR Participant ":Ui" as Ui +end box +box LOGIC_COLOR Participant ":Parser" as Parser Participant ":XYZCommand" as XYZCommand +end box + -> Decodex: run() diff --git a/docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml b/docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml index f679fe9b1c..fc445966c9 100644 --- a/docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/DecodexInitSequenceDiagram.puml @@ -1,13 +1,27 @@ @startuml !include ../Style.puml +box DECODEX_COLOR Participant "<>\nDecodex" as DecodexClass Participant ":Decodex" as Decodex +end box +box DATA_COLOR Participant ":DataManager" as DataManager +end box +box MODULE_COLOR Participant ":ModuleManager" as ModuleManager +end box +box RECIPE_COLOR Participant ":RecipeManager" as RecipeManager +end box +box LOGIC_COLOR Participant ":Parser" as Parser +end box +box STORAGE_COLOR Participant ":Storage" as Storage +end box +box UI_COLOR Participant ":Ui" as Ui +endbox -> DecodexClass: main() activate DecodexClass diff --git a/docs/puml/sequencediagrams/ParserSequenceDiagram.puml b/docs/puml/sequencediagrams/ParserSequenceDiagram.puml index 12b18bd4ee..257a1cff6c 100644 --- a/docs/puml/sequencediagrams/ParserSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/ParserSequenceDiagram.puml @@ -1,8 +1,10 @@ @startuml !include ../Style.puml +box LOGIC_COLOR Participant ":Parser" as Parser Participant ":XYZCommand" as XYZCommand +end box activate Parser -> Parser : parseCommand(userInput : String) diff --git a/docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml index ef00e61b93..c315840afe 100644 --- a/docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/ExitCommandSequenceDiagram.puml @@ -1,8 +1,12 @@ @startuml !include ../../Style.puml +box LOGIC_COLOR Participant ":ExitCommand" as ExitCommand +end box +box UI_COLOR Participant ":Ui" as Ui +endbox -> ExitCommand: run() activate ExitCommand diff --git a/docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml index 92a735bf46..0664c62d40 100644 --- a/docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/InputCommandSequenceDiagram.puml @@ -1,10 +1,16 @@ @startuml !include ../../Style.puml +box LOGIC_COLOR Participant ":InputCommand" as InputCommand +end box +box DATA_COLOR Participant ":Data" as Data Participant ":DataManager" as DataManager +end box +box UI_COLOR Participant ":Ui" as Ui +end box -> InputCommand: run() activate InputCommand diff --git a/docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml index 43caa07181..a681bac23a 100644 --- a/docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/ResetCommandSequenceDiagram.puml @@ -1,9 +1,15 @@ @startuml !include ../../Style.puml +box LOGIC_COLOR Participant ":ResetCommand" as ResetCommand +end box +box DATA_COLOR Participant ":DataManager" as DataManager +end box +box UI_COLOR Participant ":Ui" as Ui +end box -> ResetCommand: run() diff --git a/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml index a09bfeb272..bf6eb4db0f 100644 --- a/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml @@ -1,11 +1,19 @@ @startuml !include ../../Style.puml +box LOGIC_COLOR Participant ":SelectCommand" as SelectCommand +end box +box MODULE_COLOR Participant ":ModuleManager" as ModuleManager -Participant ":DataManager" as DataManager Participant ":Module" as Module +end box +box DATA_COLOR +Participant ":DataManager" as DataManager +end box +box UI_COLOR Participant ":Ui" as Ui +end box -> SelectCommand: run() activate SelectCommand From 92b6f6d41262a3b76328d6a12990bb06553c6d2d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 21:32:47 +0800 Subject: [PATCH 291/607] Added the missing static keywords --- src/main/java/decodex/storage/Storage.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index c7e4c58acb..337300e90a 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -20,14 +20,14 @@ public class Storage { * 2. output folder - for writing processed data into file. * 3. recipe folder - for reading and writing recipe from and to file. */ - private final String [] DEFAULT_DIRECTORY_LIST = {"input", "output", "recipe"}; + private static final String [] DEFAULT_DIRECTORY_LIST = {"input", "output", "recipe"}; /** * The indexes of the corresponding directory in the list. */ - private final int INPUT_DIRECTORY_INDEX = 0; - private final int OUTPUT_DIRECTORY_INDEX = 1; - private final int RECIPE_DIRECTORY_INDEX = 2; + private static final int INPUT_DIRECTORY_INDEX = 0; + private static final int OUTPUT_DIRECTORY_INDEX = 1; + private static final int RECIPE_DIRECTORY_INDEX = 2; /** * Initializes a new Storage. From 67b2dc169adbde2f56fb06426ab50fb2ee8f7bc2 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 22:39:09 +0800 Subject: [PATCH 292/607] Fix exception error --- src/main/java/decodex/commands/RecipeNewCommand.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeNewCommand.java b/src/main/java/decodex/commands/RecipeNewCommand.java index cc8ff89690..0e558c2d38 100644 --- a/src/main/java/decodex/commands/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/RecipeNewCommand.java @@ -4,7 +4,6 @@ import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; @@ -24,7 +23,7 @@ public RecipeNewCommand(String recipeName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, UnknownModuleException, ModuleException, RecipeManagerException { + throws CommandException, ModuleException, RecipeManagerException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } From 777ae3dbdbede8931af2b0a6c3ba17010f906977 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 22:41:59 +0800 Subject: [PATCH 293/607] Fix exception error --- src/main/java/decodex/commands/RecipeDeleteCommand.java | 3 +-- src/test/java/decodex/commands/RecipeDeleteCommandTest.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeDeleteCommand.java b/src/main/java/decodex/commands/RecipeDeleteCommand.java index 60faf31936..994628d691 100644 --- a/src/main/java/decodex/commands/RecipeDeleteCommand.java +++ b/src/main/java/decodex/commands/RecipeDeleteCommand.java @@ -4,7 +4,6 @@ import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.ui.Ui; @@ -23,7 +22,7 @@ public RecipeDeleteCommand(String recipeName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, UnknownModuleException, ModuleException, RecipeManagerException { + throws CommandException, ModuleException, RecipeManagerException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java index a40ba8d088..161c7b1834 100644 --- a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java @@ -6,9 +6,9 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; @@ -19,8 +19,8 @@ class RecipeDeleteCommandTest { @Test void run_deleteValidRecipe_recipeDeleted() - throws UnknownModuleException, CommandException, RecipeException, - ModuleException, RecipeManagerException, DataManagerException { + throws CommandException, RecipeException, + ModuleException, RecipeManagerException, DataManagerException, ModuleManagerException { String testRecipeName = "BaconPancakes"; DataManager dataManager = new DataManager(); From 1b6519913a003a10dab00ac3227b07a5b2715a0e Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 22:43:08 +0800 Subject: [PATCH 294/607] Fix exception error Fix exception error (for real this time) --- src/test/java/decodex/commands/RecipeNewCommandTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/commands/RecipeNewCommandTest.java index 177ded476a..5eb868c4f6 100644 --- a/src/test/java/decodex/commands/RecipeNewCommandTest.java +++ b/src/test/java/decodex/commands/RecipeNewCommandTest.java @@ -7,9 +7,9 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.UnknownModuleException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.ui.Ui; @@ -19,8 +19,8 @@ class RecipeNewCommandTest { @Test void run_createNewRecipe_newRecipeCreated() - throws UnknownModuleException, CommandException, RecipeException, - ModuleException, RecipeManagerException, DataManagerException { + throws CommandException, RecipeException, + ModuleException, RecipeManagerException, DataManagerException, ModuleManagerException { String testRecipeName = "BaconPancakes"; Command newRecipeCommand = new RecipeNewCommand(testRecipeName); From 8c7033b37d2efabe076e4a809af7a4cd957997be Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 23:01:00 +0800 Subject: [PATCH 295/607] Add requested Changes --- src/main/java/decodex/commands/RecipeNewCommand.java | 2 ++ src/test/java/decodex/commands/RecipeNewCommandTest.java | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeNewCommand.java b/src/main/java/decodex/commands/RecipeNewCommand.java index 0e558c2d38..a40bc85a00 100644 --- a/src/main/java/decodex/commands/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/RecipeNewCommand.java @@ -31,6 +31,8 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec Recipe recipe = new Recipe(recipeName); recipeManager.addRecipe(recipe); + recipeManager.selectRecipeForEditing(recipeName); + ui.printNewRecipeCreated(recipe.getName()); } } diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/commands/RecipeNewCommandTest.java index 5eb868c4f6..1702a7fb16 100644 --- a/src/test/java/decodex/commands/RecipeNewCommandTest.java +++ b/src/test/java/decodex/commands/RecipeNewCommandTest.java @@ -18,9 +18,8 @@ class RecipeNewCommandTest { @Test - void run_createNewRecipe_newRecipeCreated() - throws CommandException, RecipeException, - ModuleException, RecipeManagerException, DataManagerException, ModuleManagerException { + void run_createNewRecipe_newRecipeCreated() throws CommandException, RecipeException, ModuleException, + RecipeManagerException, DataManagerException, ModuleManagerException { String testRecipeName = "BaconPancakes"; Command newRecipeCommand = new RecipeNewCommand(testRecipeName); From 515c6ff8e3d71c9139f7c9b5d098c855b1629086 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 23:04:35 +0800 Subject: [PATCH 296/607] Add requested changes --- src/test/java/decodex/commands/RecipeDeleteCommandTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java index 161c7b1834..bc9707f9be 100644 --- a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java @@ -18,9 +18,8 @@ class RecipeDeleteCommandTest { @Test - void run_deleteValidRecipe_recipeDeleted() - throws CommandException, RecipeException, - ModuleException, RecipeManagerException, DataManagerException, ModuleManagerException { + void run_deleteValidRecipe_recipeDeleted() throws CommandException, RecipeException, ModuleException, + RecipeManagerException, DataManagerException, ModuleManagerException { String testRecipeName = "BaconPancakes"; DataManager dataManager = new DataManager(); @@ -38,7 +37,6 @@ void run_deleteValidRecipe_recipeDeleted() @Test void run_deleteInvalidRecipe_expectException() throws RecipeManagerException { - DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); From f912bfb61b93982f480e54f3086c39210e1fd977 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 23:11:40 +0800 Subject: [PATCH 297/607] Removed the author lines temporarily - kindda annoying seeing it everywhere --- src/main/java/decodex/storage/Storage.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 337300e90a..f297440d4a 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -35,7 +35,6 @@ public class Storage { public Storage() { } - // @@author Kair0s3 /** * Reads and returns the input from the provided file. * @@ -55,7 +54,6 @@ private byte[] readInputFromFile(String fileName) throws IOException { return inputContent; } - // @@author Kair0s3 /** * Reads and returns the recipe from the provided file. * @@ -76,7 +74,6 @@ private String readRecipeFromFile(String fileName) throws IOException { return recipeContent; } - // @@author Kair0s3 /** * Reads and returns the contents. * @@ -97,7 +94,6 @@ private byte[] readContent(Path readFilePath) throws IOException, FileNotFoundEx } } - // @@author Kair0s3 /** * Writes the given output into a file. * @@ -124,7 +120,6 @@ private void writeOutputToFile(String output) throws IOException { } } - // @@author Kair0s3 /** * Instantiates the given directory if it does not exist yet. From 60f6fc06c10df14924b453016a01ac7696c251b8 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 23 Oct 2021 23:41:33 +0800 Subject: [PATCH 298/607] Add recipelistcommand --- docs/carbon/recipelistcommand.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/carbon/recipelistcommand.txt diff --git a/docs/carbon/recipelistcommand.txt b/docs/carbon/recipelistcommand.txt new file mode 100644 index 0000000000..89ea703a2f --- /dev/null +++ b/docs/carbon/recipelistcommand.txt @@ -0,0 +1,7 @@ +Decodex [] > recipe list testRecipe +Here are the list of modules in recipe testRecipe + 1. hexencode + 2. base64encode + 3. base64encode + +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520recipe%2520list%2520testRecipe%250AHere%2520are%2520the%2520list%2520of%2520modules%2520in%2520recipe%2520testRecipe%250A%2520%25201.%2520hexencode%250A%2520%25202.%2520base64encode%250A%2520%25203.%2520base64encode \ No newline at end of file From ec5895eb9f6a64bd7e22d22a8ab5688fbad66c45 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 23:43:20 +0800 Subject: [PATCH 299/607] Removed the static message for the IOEXCEPTION --- src/main/java/decodex/storage/Storage.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index f297440d4a..a86e72aaae 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -127,12 +127,13 @@ private void writeOutputToFile(String output) throws IOException { private void instantiateDirectoryIfNotExist(String directoryName) throws IOException { File outputDirectory = new File(directoryName); boolean isSuccessful = false; - if (!outputDirectory.exists()) { - isSuccessful = outputDirectory.mkdir(); + if (outputDirectory.exists()) { + return; } + isSuccessful = outputDirectory.mkdir(); if (!isSuccessful) { - throw new IOException("Failed to create the directory for " + directoryName); + throw new IOException(ErrorMessages.DIRECTORY_INSTANTIATION_FAILED_MESSAGE + directoryName); } } } From 6a159f73b2ef3596f65ef7f5a61119f676a27bb8 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 23 Oct 2021 23:43:34 +0800 Subject: [PATCH 300/607] Added some additional error messages. --- src/main/java/decodex/ui/messages/ErrorMessages.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index f897c2e34c..51a997d659 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -25,4 +25,6 @@ public class ErrorMessages { public static final String INPUT_FILE_DOES_NOT_EXIST_MESSAGE = "The default input file does not exists"; public static final String FILE_WRITE_ERROR_MESSAGE = "Something went wrong while writing result to output file"; + public static final String FILE_READ_ERROR_MESSAGE = "Something went wrong when reading from the file"; + public static final String DIRECTORY_INSTANTIATION_FAILED_MESSAGE = "Failed to create the directory for "; } From 07e00e5589541748c3321a0e60d71a07232b8701 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sat, 23 Oct 2021 23:50:27 +0800 Subject: [PATCH 301/607] Add requested changes --- src/main/java/decodex/recipes/Recipe.java | 21 +++++-------- src/test/java/decodex/recipes/RecipeTest.java | 31 ++++++++++++------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index c0e396baea..d49894963f 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -75,23 +75,16 @@ public Module pop() throws RecipeException { /** * Runs the current recipe. * - * @param dataManager The dataManager containing the data to be manipulated. - * @param ui The Ui object handling any ui operations. - * @throws RecipeException If there are no modules in the recipe. - * @throws ModuleException If the selected module cannot be found. - * @throws DataManagerException If there is no data to be found. + * @param data The data to run through the recipe. + * @return A new data object that has gone through all the recipe modules. + * @throws ModuleException If the selected module cannot be found or fails. */ - public void run(DataManager dataManager, Ui ui) throws RecipeException, ModuleException, DataManagerException { - if (moduleList.isEmpty()) { - throw new RecipeException(ErrorMessages.EMPTY_RECIPE_MESSAGE); - } - + public Data run(Data data) throws ModuleException { + Data bakedData = data; for (Module module : moduleList) { - Data newData = module.run(dataManager.getCurrentData()); - dataManager.setCurrentData(newData); - - ui.printOutput(newData.toString()); + bakedData = module.run(bakedData); } + return bakedData; } /** diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index 67088a1d39..9892c4e99f 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import decodex.data.Data; -import decodex.data.DataManager; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; @@ -16,8 +15,8 @@ import decodex.modules.hex.HexEncoder; import decodex.modules.Module; import decodex.modules.rot.RotEncoder; -import decodex.ui.Ui; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; class RecipeTest { @@ -45,7 +44,7 @@ void pop_singleModule_latestModule() throws RecipeException { void pop_emptyRecipe_expectException() { Recipe recipe = new Recipe("testRecipe"); - assertThrows(RecipeException.class, () -> recipe.pop()); + assertThrows(RecipeException.class, recipe::pop); } @Test @@ -62,7 +61,7 @@ void reset_emptyModuleList() { } @Test - void run_chainOfModules_encodedOutput() throws RecipeException, ModuleException, DataManagerException { + void run_chainOfModules_encodedOutput() throws ModuleException { Recipe recipe = new Recipe("rainbowTest"); recipe.push(new HexEncoder()); @@ -70,18 +69,28 @@ void run_chainOfModules_encodedOutput() throws RecipeException, ModuleException, recipe.push(new RotEncoder(5)); recipe.push(new BinaryEncoder()); - DataManager dataManager = new DataManager(); Data inputData = new Data("egg"); - dataManager.setOriginalData(inputData); - Ui ui = new Ui(); - - recipe.run(dataManager, ui); - - String result = dataManager.getCurrentData().toString(); + Data bakedData = recipe.run(inputData); + String result = bakedData.toString(); String expectedOutput = "0101001101101111010110100011001001010011011001010100010000110011"; assertEquals(result, expectedOutput); } + @Test() + void run_chainOfModulesFailHalfway_expectException() { + Recipe recipe = new Recipe("rainbowTest"); + + recipe.push(new HexEncoder()); + recipe.push(new Base64Encoder()); + recipe.push(new RotEncoder(5)); + recipe.push(new HexDecoder()); + recipe.push(new BinaryEncoder()); + + Data inputData = new Data("egg"); + + assertThrows(ModuleException.class, () -> recipe.run(inputData)); + } + } From f0a9c9e3c365c299d3713f40d2d0cdaa8a5b46c5 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Sun, 24 Oct 2021 00:09:22 +0800 Subject: [PATCH 302/607] Add requested changes --- src/test/java/decodex/recipes/RecipeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index 9892c4e99f..3a1ca295e7 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -44,7 +44,7 @@ void pop_singleModule_latestModule() throws RecipeException { void pop_emptyRecipe_expectException() { Recipe recipe = new Recipe("testRecipe"); - assertThrows(RecipeException.class, recipe::pop); + assertThrows(RecipeException.class, () -> recipe.pop()); } @Test From 053745f5d791b7aa10c5aa779617182a6a1fe1ce Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 24 Oct 2021 00:09:37 +0800 Subject: [PATCH 303/607] Add the functionlities to read and write to file. Also removed "magic literals". --- src/main/java/decodex/storage/Storage.java | 86 +++++++++++++++------- 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index a86e72aaae..2c0a49cc56 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.FileNotFoundException; -import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -20,7 +19,7 @@ public class Storage { * 2. output folder - for writing processed data into file. * 3. recipe folder - for reading and writing recipe from and to file. */ - private static final String [] DEFAULT_DIRECTORY_LIST = {"input", "output", "recipe"}; + private static final String[] DEFAULT_DIRECTORY_LIST = {"input", "output", "recipe"}; /** * The indexes of the corresponding directory in the list. @@ -29,6 +28,10 @@ public class Storage { private static final int OUTPUT_DIRECTORY_INDEX = 1; private static final int RECIPE_DIRECTORY_INDEX = 2; + private static final String TEXT_FILE_PREFIX = ".txt"; + + private static final String RECIPE_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; + /** * Initializes a new Storage. */ @@ -38,95 +41,124 @@ public Storage() { /** * Reads and returns the input from the provided file. * - * @param fileName The name of the file specified by the user. - * @return The byte contents from the default input file. + * @param fileName The name of the input file specified by the user. + * @return The byte contents from the input file. * @throws IOException If something went wrong when reading the file or * the input file does not exist. */ - private byte[] readInputFromFile(String fileName) throws IOException { + public byte[] readInputFromFile(String fileName) throws IOException { instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); File inputDirectory = new File(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); File inputFile = new File(inputDirectory, fileName); Path inputFilePath = inputFile.toPath(); - byte[] inputContent = readContent(inputFilePath); + byte[] inputContent = readFromFile(inputFilePath); return inputContent; } /** * Reads and returns the recipe from the provided file. * - * @param fileName The name of the file specified by the user. + * @param fileName The name of the recipe file specified by the user. * @return The string formatted recipe from the provided file. * @throws IOException If something went wrong when reading the file or * the input file does not exist. */ - private String readRecipeFromFile(String fileName) throws IOException { + public String readRecipeFromFile(String fileName) throws IOException { instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); File recipeDirectory = new File(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); File recipeFile = new File(recipeDirectory, fileName); Path recipeFilePath = recipeFile.toPath(); - byte[] recipeContentBytes = readContent(recipeFilePath); + byte[] recipeContentBytes = readFromFile(recipeFilePath); String recipeContent = recipeContentBytes.toString(); return recipeContent; } /** - * Reads and returns the contents. + * Reads and returns the contents from the file. * * @param readFilePath The Path object of the file to be read. * @return The bytes of the read content. * @throws IOException If something went wrong when reading the file. * @throws FileNotFoundException If the input file does not exist. */ - private byte[] readContent(Path readFilePath) throws IOException, FileNotFoundException { + private byte[] readFromFile(Path readFilePath) throws IOException, FileNotFoundException { try { byte[] inputContent; inputContent = Files.readAllBytes(readFilePath); return inputContent; - } catch (FileNotFoundException fileNotFoundError) { + } catch (FileNotFoundException err) { throw new FileNotFoundException(ErrorMessages.INPUT_FILE_DOES_NOT_EXIST_MESSAGE); - } catch (IOException ioException) { - throw new IOException("Error during file read"); + } catch (IOException err) { + throw new IOException(ErrorMessages.FILE_READ_ERROR_MESSAGE); } } /** - * Writes the given output into a file. + * Writes the provided output into a file. * - * @param output The encoded or decoded output. - * @throws IOException If an I/O exception is caught when creating the output file - * or when writing to the output file. + * @param outputBytes The encoded or decoded output. + * @throws IOException If something went wrong when creating the file + * or when writing to the file. */ - private void writeOutputToFile(String output) throws IOException { + public void writeOutputToFile(byte[] outputBytes) throws IOException { + instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[OUTPUT_DIRECTORY_INDEX]); + LocalDateTime currentDateTime = LocalDateTime.now(); - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-dd-MM__HH.mm.ss"); + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(RECIPE_FILENAME_FORMAT); String formattedDateTime = currentDateTime.format(dateTimeFormatter); - String newOutputFileName = formattedDateTime + ".txt"; + String newOutputFileName = formattedDateTime + TEXT_FILE_PREFIX; File outputDirectory = new File(DEFAULT_DIRECTORY_LIST[OUTPUT_DIRECTORY_INDEX]); File outputFile = new File(outputDirectory, newOutputFileName); + writeToFile(outputFile, outputBytes); + } + + /** + * Writes the provided recipe into a file. + * + * @param recipeName The name of the recipe. + * @param recipeBytes The recipe formatted in bytes. + * @throws IOException If something went wrong when creating the file + * or when writing to the file. + */ + public void writeRecipeToFile(String recipeName, byte[] recipeBytes) throws IOException { + instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + String newRecipeFileName = recipeName + TEXT_FILE_PREFIX; + + File recipeDirectory = new File(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); + writeToFile(outputRecipeFile, recipeBytes); + } + + /** + * Writes the contents into the provided file. + * + * @param writeFile The file to be written to. + * @param contentBytes The bytes of the content to be written. + * @throws IOException If something went wrong when creating the file + * or when writing to the file. + */ + private void writeToFile(File writeFile, byte[] contentBytes) throws IOException { try { - outputFile.createNewFile(); - FileWriter fw = new FileWriter(outputFile); - fw.write(output + "\n"); - fw.close(); + writeFile.createNewFile(); + Path writeFilePath = writeFile.toPath(); + Files.write(writeFilePath, contentBytes); } catch (IOException e) { throw new IOException(ErrorMessages.FILE_WRITE_ERROR_MESSAGE); } } - /** * Instantiates the given directory if it does not exist yet. */ private void instantiateDirectoryIfNotExist(String directoryName) throws IOException { File outputDirectory = new File(directoryName); - boolean isSuccessful = false; + boolean isSuccessful; if (outputDirectory.exists()) { return; } From 8286f19b6da5d3bdf5b6f4002d8a6575816cfbe8 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sun, 24 Oct 2021 00:27:26 +0800 Subject: [PATCH 304/607] Add transparency --- docs/puml/Style.puml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/puml/Style.puml b/docs/puml/Style.puml index f368f1fb22..99bff12b0d 100644 --- a/docs/puml/Style.puml +++ b/docs/puml/Style.puml @@ -16,4 +16,5 @@ hide members hide circle skinparam DefaultTextAlignment center -skinparam Shadowing false \ No newline at end of file +skinparam Shadowing false +skinparam SequenceGroupBodyBackGroundColor ffffff90 \ No newline at end of file From efc33c6d2f9dd30276f4c625e9714a91486defa4 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 24 Oct 2021 00:36:33 +0800 Subject: [PATCH 305/607] Update `SelectCommand` to support selecting recipes * Update selection of modules and recipes for `SelectCommand` * Update parsing for `ListCommand` to follow similar process as `SelectCommand` * Update `ParserTest` and `SelectCommandTest` --- .../java/decodex/commands/SelectCommand.java | 71 ++++++++++- src/main/java/decodex/parser/Parser.java | 54 +++++--- src/main/java/decodex/recipes/Recipe.java | 2 + .../decodex/ui/messages/ErrorMessages.java | 1 + .../decodex/commands/SelectCommandTest.java | 120 ++++++++++++++++-- src/test/java/decodex/parser/ParserTest.java | 4 +- 6 files changed, 221 insertions(+), 31 deletions(-) diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index a65f63b49b..9eb1ed6607 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -5,11 +5,12 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.ModuleManagerException; import decodex.modules.Module; import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -18,12 +19,17 @@ public class SelectCommand extends Command { public static final String COMMAND_WORD = "select"; - private final String moduleName; + private static final String SELECT_CATEGORY_MODULE = "module"; + private static final String SELECT_CATEGORY_RECIPE = "recipe"; + + private final String selectCategory; + private final String itemName; private final String[] parameters; - public SelectCommand(String moduleName, String[] parameters) { + public SelectCommand(String selectCategory, String itemName, String[] parameters) { super(); - this.moduleName = moduleName; + this.selectCategory = selectCategory; + this.itemName = itemName; this.parameters = parameters; } @@ -31,14 +37,67 @@ public SelectCommand(String moduleName, String[] parameters) { public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, RecipeManagerException { - if (moduleName.isBlank()) { + assert selectCategory != null : "selectCategory should not be null"; + assert itemName != null : "itemName should not be null"; + assert parameters != null : "parameters should not be null"; + + switch (selectCategory) { + case SELECT_CATEGORY_MODULE: + selectModule(dataManager, moduleManager, ui); + break; + case SELECT_CATEGORY_RECIPE: + selectRecipe(dataManager, recipeManager, ui); + break; + default: + throw new CommandException(ErrorMessages.UNKNOWN_SELECTION_CATEGORY); + } + } + + /** + * Selects and runs a module. + * + * @param dataManager The provided DataManager. + * @param moduleManager The provided ModuleManager. + * @param ui The provided Ui. + * @throws CommandException If the command parameters are invalid. + * @throws ModuleManagerException If the selected module is invalid. + * @throws ModuleException If the module execution has failed. + * @throws DataManagerException If the data is invalid. + */ + private void selectModule(DataManager dataManager, ModuleManager moduleManager, Ui ui) + throws CommandException, ModuleManagerException, ModuleException, DataManagerException { + if (itemName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); } - Module module = moduleManager.selectModule(moduleName, parameters); + Module module = moduleManager.selectModule(itemName, parameters); Data newData = module.run(dataManager.getCurrentData()); dataManager.setCurrentData(newData); ui.printOutput(newData.toString()); } + + /** + * Selects and runs a module. + * + * @param dataManager The provided DataManager. + * @param recipeManager The provided RecipeManager. + * @param ui The provided Ui. + * @throws CommandException If the command parameters are invalid. + * @throws RecipeManagerException If the recipe is invalid. + * @throws ModuleException If the module execution has failed. + * @throws DataManagerException If the data is invalid. + */ + private void selectRecipe(DataManager dataManager, RecipeManager recipeManager, Ui ui) + throws CommandException, RecipeManagerException, ModuleException, DataManagerException { + if (itemName.isBlank()) { + throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); + } + + Recipe recipe = recipeManager.getRecipe(itemName); + Data newData = recipe.run(dataManager.getCurrentData()); + dataManager.setCurrentData(newData); + + ui.printOutput(newData.toString()); + } } diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index dcdfdcbb2a..65fdd1c87a 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -44,11 +44,19 @@ public class Parser { private static final int VALID_TOKENS_LENGTH_FOR_COMMAND = 1; /** - * Specifies the number of arguments for list command with no category and with a category. + * Specifies the number of tokens for list command with no category and with a category. */ - private static final int LIST_ARGUMENT_LENGTH_NO_CATEGORY = 0; - private static final int LIST_ARGUMENT_LENGTH_HAS_CATEGORY = 1; - private static final int LIST_ARGUMENT_STARTING_INDEX = 0; + private static final int LIST_COMMAND_LENGTH_NO_CATEGORY = 1; + private static final int LIST_COMMAND_LENGTH_HAS_CATEGORY = 2; + private static final int LIST_COMMAND_CATEGORY_STARTING_INDEX = 1; + + /** + * Specifies the valid number of tokens and indexes for category and parameters for select command. + */ + private static final int VALID_TOKENS_LENGTH_FOR_SELECT_COMMAND = 3; + private static final int SELECT_CATEGORY_INDEX = 1; + private static final int SELECT_ITEM_NAME_INDEX = 2; + private static final int SELECT_PARAMETERS_START_INDEX = 3; /** * Returns the type of command that the user has specified. @@ -162,6 +170,17 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc return command; } + /** + * Splits the user input by an arbitrary number of spaces into a list of tokens. + * + * @param userInput The user input specified by the user. + * @return A list of tokens. + */ + private String[] getTokens(String userInput) { + String strippedUserInput = userInput.stripLeading(); + return strippedUserInput.split(SPLIT_REGEX); + } + /** * Prepares and returns the ExitCommand. * @@ -191,13 +210,12 @@ private InputCommand craftInputCommand(String userInput) throws ParserException * @throws CommandException If the command has more than 1 argument. */ private String getListCategory(String userInput) throws CommandException { - String[] tokens = userInput.split(SPLIT_REGEX); - String[] arguments = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); + String[] tokens = getTokens(userInput); - if (arguments.length == LIST_ARGUMENT_LENGTH_NO_CATEGORY) { + if (tokens.length == LIST_COMMAND_LENGTH_NO_CATEGORY) { return null; - } else if (arguments.length == LIST_ARGUMENT_LENGTH_HAS_CATEGORY) { - return arguments[LIST_ARGUMENT_STARTING_INDEX]; + } else if (tokens.length == LIST_COMMAND_LENGTH_HAS_CATEGORY) { + return tokens[LIST_COMMAND_CATEGORY_STARTING_INDEX]; } else { throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); } @@ -231,12 +249,18 @@ private ResetCommand craftResetCommand() { * * @param userInput The user input specified by the user. * @return The SelectCommand object. - * @throws ParserException If the user input is missing the module name. + * @throws CommandException If the number of command arguments is invalid. */ - private SelectCommand craftSelectCommand(String userInput) throws ParserException { - String moduleName = getModuleName(userInput); - String[] parameters = getModuleParameters(userInput); - return new SelectCommand(moduleName, parameters); - } + private SelectCommand craftSelectCommand(String userInput) throws CommandException { + String[] tokens = getTokens(userInput); + if (tokens.length < VALID_TOKENS_LENGTH_FOR_SELECT_COMMAND) { + throw new CommandException(ErrorMessages.MISSING_ARGUMENT); + } + + String selectCategory = tokens[SELECT_CATEGORY_INDEX]; + String itemName = tokens[SELECT_ITEM_NAME_INDEX]; + String[] parameters = Arrays.copyOfRange(tokens, SELECT_PARAMETERS_START_INDEX, tokens.length); + return new SelectCommand(selectCategory, itemName, parameters); + } } diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index d49894963f..56eb5f5c7a 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -9,6 +9,8 @@ import java.util.logging.Logger; import decodex.Decodex; +import decodex.data.Data; +import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.modules.Module; import decodex.ui.messages.ErrorMessages; diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 9c68f085cf..eaa03bd9aa 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -22,4 +22,5 @@ public class ErrorMessages { public static final String INVALID_MODULE_PARAMETERS = "Invalid module parameters"; public static final String MISSING_MODULE_PARAMETERS = "Missing module parameters"; public static final String TOO_MANY_MODULE_PARAMETERS = "Too many module parameters"; + public static final String UNKNOWN_SELECTION_CATEGORY = "Unknown selection category"; } diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index 00e046ca12..a1efaede31 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -1,21 +1,23 @@ package decodex.commands; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import decodex.data.Data; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; +import decodex.modules.base64.Base64Encoder; +import decodex.modules.hex.HexEncoder; +import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class SelectCommandTest { @@ -31,9 +33,10 @@ public void run_validModuleName_success() Data data = new Data("hello world"); dataManager.setOriginalData(data); + String selectCategory = "module"; String moduleName = "base64encode"; String[] parameters = {}; - SelectCommand selectCommand = new SelectCommand(moduleName, parameters); + SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); selectCommand.run(dataManager, moduleManager, ui, recipeManager); assertEquals("aGVsbG8gd29ybGQ=", dataManager.getCurrentData().toString()); @@ -51,9 +54,10 @@ public void run_moduleWithParameters_success() Data data = new Data("hello world"); dataManager.setOriginalData(data); + String selectCategory = "module"; String moduleName = "rotencode"; String[] parameters = {"13"}; - SelectCommand selectCommand = new SelectCommand(moduleName, parameters); + SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); selectCommand.run(dataManager, moduleManager, ui, recipeManager); assertEquals("uryyb jbeyq", dataManager.getCurrentData().toString()); @@ -69,9 +73,10 @@ public void run_blankModuleName_expectException() { Data data = new Data("hello world"); dataManager.setOriginalData(data); + String selectCategory = "module"; String moduleName = ""; String[] parameters = {}; - SelectCommand selectCommand = new SelectCommand(moduleName, parameters); + SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); } @@ -86,11 +91,110 @@ public void run_unknownModuleName_expectException() { Data data = new Data("hello world"); dataManager.setOriginalData(data); + String selectCategory = "module"; String moduleName = "unknownModule"; String[] parameters = {}; - SelectCommand selectCommand = new SelectCommand(moduleName, parameters); + SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); assertThrows(ModuleManagerException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); } + + @Test + public void run_validRecipeName_success() + throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, + RecipeManagerException { + DataManager dataManager = new DataManager(); + Data data = new Data("hello world"); + dataManager.setOriginalData(data); + + RecipeManager recipeManager = new RecipeManager(); + String recipeName = "testRecipe"; + Recipe recipe = new Recipe(recipeName); + recipe.push(new HexEncoder()); + recipe.push(new Base64Encoder()); + recipeManager.addRecipe(recipe); + + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + String selectCategory = "recipe"; + String[] parameters = {}; + SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); + selectCommand.run(dataManager, moduleManager, ui, recipeManager); + + assertEquals("Njg2NTZjNmM2ZjIwNzc2ZjcyNmM2NA==", dataManager.getCurrentData().toString()); + } + + @Test + public void run_emptyRecipe_success() + throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, + RecipeManagerException { + DataManager dataManager = new DataManager(); + String dataString = "hello world"; + Data data = new Data(dataString); + dataManager.setOriginalData(data); + + RecipeManager recipeManager = new RecipeManager(); + String recipeName = "testRecipe"; + Recipe recipe = new Recipe(recipeName); + recipeManager.addRecipe(recipe); + + ModuleManager moduleManager = new ModuleManager(); + Ui ui = new Ui(); + + String selectCategory = "recipe"; + String[] parameters = {}; + SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); + selectCommand.run(dataManager, moduleManager, ui, recipeManager); + + assertEquals(dataString, dataManager.getCurrentData().toString()); + } + + @Test + public void run_blankRecipeName_expectRecipeManagerException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String selectCategory = "recipe"; + String recipeName = "testRecipe"; + String[] parameters = {}; + SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); + + assertThrows(RecipeManagerException.class, () -> selectCommand.run(dataManager, moduleManager, ui, + recipeManager)); + } + + @Test + public void run_unknownRecipe_expectRecipeManagerException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String selectCategory = "recipe"; + String recipeName = "unknownRecipe"; + String[] parameters = {}; + SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); + + assertThrows(RecipeManagerException.class, () -> selectCommand.run(dataManager, moduleManager, ui, + recipeManager)); + } + + @Test + public void run_invalidSelectCategory_expectCommandException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String selectCategory = "unknownCategory"; + String recipeName = "unknownItem"; + String[] parameters = {}; + SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); + + assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); + } } diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index bd269d02d8..2335c0846e 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -106,9 +106,9 @@ public void parseCommand_userInputSpecifyingReset_expectResetCommand() throws Pa } @Test - public void parseCommand_userInputSpecifyingValidSelect_expectSelectCommand() throws ParserException, + public void parseCommand_userInputSpecifyingValidSelectModule_expectSelectCommand() throws ParserException, CommandException { - String userInput = "select dummyMod"; + String userInput = "select module base64encode"; Command command = parser.parseCommand(userInput); assertTrue(command instanceof SelectCommand); } From c5fd63e6cc1a258269e1137b46cca0bc18d7a3a9 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 24 Oct 2021 00:42:32 +0800 Subject: [PATCH 306/607] Update text-ui-test --- text-ui-test/EXPECTED.TXT | 3 +++ text-ui-test/input.txt | 11 +++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index b06bc82f03..ac99c91ed7 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -7,6 +7,9 @@ Decodex > [+] Input: "I am a lab rat" Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" Decodex > [+] Output: "I am a lab rat" Decodex > [+] Output: "4920616d2061206c616220726174" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "L dp d ode udw" +Decodex > [+] Output: "I am a lab rat" Decodex > [+] Reverted all changes Decodex > [x] Unknown command, please enter a valid command Decodex > Goodbye! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index b8f535569f..724c829135 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,9 +1,12 @@ whoami bring me thanos input I am a lab rat -select base64encode -select base64decode -select hexencode +select module base64encode +select module base64decode +select module hexencode +select module hexdecode +select module rotencode 3 +select module rotencode -3 reset bye -exit \ No newline at end of file +exit From 5d5771061a9459dd3c0ff681c44bc6c249c62c1f Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 24 Oct 2021 00:45:38 +0800 Subject: [PATCH 307/607] Some changes according to the suggested changes. --- src/main/java/decodex/storage/Storage.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 2c0a49cc56..60930f1454 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -29,7 +29,7 @@ public class Storage { private static final int RECIPE_DIRECTORY_INDEX = 2; private static final String TEXT_FILE_PREFIX = ".txt"; - + private static final String RECIPE_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; /** @@ -87,9 +87,7 @@ public String readRecipeFromFile(String fileName) throws IOException { */ private byte[] readFromFile(Path readFilePath) throws IOException, FileNotFoundException { try { - byte[] inputContent; - inputContent = Files.readAllBytes(readFilePath); - return inputContent; + return Files.readAllBytes(readFilePath); } catch (FileNotFoundException err) { throw new FileNotFoundException(ErrorMessages.INPUT_FILE_DOES_NOT_EXIST_MESSAGE); } catch (IOException err) { From 9a13da38e365298b62389888fa3e186860bc1804 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 24 Oct 2021 01:04:45 +0800 Subject: [PATCH 308/607] Move `throws` keyword to the same line --- .../decodex/commands/SelectCommandTest.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index a1efaede31..eec05bb6b2 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -22,9 +22,8 @@ public class SelectCommandTest { @Test - public void run_validModuleName_success() - throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, - RecipeManagerException { + public void run_validModuleName_success() throws ModuleManagerException, CommandException, DataManagerException, + ModuleException, RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); @@ -43,9 +42,8 @@ public void run_validModuleName_success() } @Test - public void run_moduleWithParameters_success() - throws ModuleManagerException, CommandException, ModuleException, DataManagerException, RecipeException, - RecipeManagerException { + public void run_moduleWithParameters_success() throws ModuleManagerException, CommandException, ModuleException, + DataManagerException, RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); @@ -101,9 +99,8 @@ public void run_unknownModuleName_expectException() { } @Test - public void run_validRecipeName_success() - throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, - RecipeManagerException { + public void run_validRecipeName_success() throws ModuleManagerException, CommandException, DataManagerException, + ModuleException, RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); Data data = new Data("hello world"); dataManager.setOriginalData(data); @@ -127,9 +124,8 @@ public void run_validRecipeName_success() } @Test - public void run_emptyRecipe_success() - throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, - RecipeManagerException { + public void run_emptyRecipe_success() throws ModuleManagerException, CommandException, DataManagerException, + ModuleException, RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); String dataString = "hello world"; Data data = new Data(dataString); From 691c997eee14920bec340cc3a403aa42e68d227b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 24 Oct 2021 01:23:00 +0800 Subject: [PATCH 309/607] Added suggested changes --- src/main/java/decodex/storage/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 60930f1454..98e27f0172 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -154,7 +154,7 @@ private void writeToFile(File writeFile, byte[] contentBytes) throws IOException /** * Instantiates the given directory if it does not exist yet. */ - private void instantiateDirectoryIfNotExist(String directoryName) throws IOException { + public void instantiateDirectoryIfNotExist(String directoryName) throws IOException { File outputDirectory = new File(directoryName); boolean isSuccessful; if (outputDirectory.exists()) { From 13d4e52ea132dee6e0ce938a3e8b8cf9a56425eb Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 24 Oct 2021 02:34:38 +0800 Subject: [PATCH 310/607] Made some of the suggested changes. Reverted back to using constants for the directories. Changed the .txt to .data instead and its respective naming. --- src/main/java/decodex/storage/Storage.java | 42 +++++++++------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 98e27f0172..1f5307919c 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -14,21 +14,13 @@ public class Storage { /** - * List of default directories used. - * 1. input folder - for reading user input from file. - * 2. output folder - for writing processed data into file. - * 3. recipe folder - for reading and writing recipe from and to file. + * Specifies the default directories for input, output and recipes respectively. */ - private static final String[] DEFAULT_DIRECTORY_LIST = {"input", "output", "recipe"}; + private static final String DEFAULT_INPUT_DIRECTORY = "input"; + private static final String DEFAULT_OUTPUT_DIRECTORY = "output"; + private static final String DEFAULT_RECIPE_DIRECTORY = "recipe"; - /** - * The indexes of the corresponding directory in the list. - */ - private static final int INPUT_DIRECTORY_INDEX = 0; - private static final int OUTPUT_DIRECTORY_INDEX = 1; - private static final int RECIPE_DIRECTORY_INDEX = 2; - - private static final String TEXT_FILE_PREFIX = ".txt"; + private static final String DATA_FILE_PREFIX = ".data"; private static final String RECIPE_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; @@ -47,9 +39,9 @@ public Storage() { * the input file does not exist. */ public byte[] readInputFromFile(String fileName) throws IOException { - instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); + instantiateDirectoryIfNotExist(DEFAULT_INPUT_DIRECTORY); - File inputDirectory = new File(DEFAULT_DIRECTORY_LIST[INPUT_DIRECTORY_INDEX]); + File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); File inputFile = new File(inputDirectory, fileName); Path inputFilePath = inputFile.toPath(); @@ -66,9 +58,9 @@ public byte[] readInputFromFile(String fileName) throws IOException { * the input file does not exist. */ public String readRecipeFromFile(String fileName) throws IOException { - instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); - File recipeDirectory = new File(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File recipeFile = new File(recipeDirectory, fileName); Path recipeFilePath = recipeFile.toPath(); @@ -103,14 +95,14 @@ private byte[] readFromFile(Path readFilePath) throws IOException, FileNotFoundE * or when writing to the file. */ public void writeOutputToFile(byte[] outputBytes) throws IOException { - instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[OUTPUT_DIRECTORY_INDEX]); + instantiateDirectoryIfNotExist(DEFAULT_OUTPUT_DIRECTORY); LocalDateTime currentDateTime = LocalDateTime.now(); DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(RECIPE_FILENAME_FORMAT); String formattedDateTime = currentDateTime.format(dateTimeFormatter); - String newOutputFileName = formattedDateTime + TEXT_FILE_PREFIX; + String newOutputFileName = formattedDateTime + DATA_FILE_PREFIX; - File outputDirectory = new File(DEFAULT_DIRECTORY_LIST[OUTPUT_DIRECTORY_INDEX]); + File outputDirectory = new File(DEFAULT_OUTPUT_DIRECTORY); File outputFile = new File(outputDirectory, newOutputFileName); writeToFile(outputFile, outputBytes); } @@ -124,11 +116,11 @@ public void writeOutputToFile(byte[] outputBytes) throws IOException { * or when writing to the file. */ public void writeRecipeToFile(String recipeName, byte[] recipeBytes) throws IOException { - instantiateDirectoryIfNotExist(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); - String newRecipeFileName = recipeName + TEXT_FILE_PREFIX; + String newRecipeFileName = recipeName + DATA_FILE_PREFIX; - File recipeDirectory = new File(DEFAULT_DIRECTORY_LIST[RECIPE_DIRECTORY_INDEX]); + File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); writeToFile(outputRecipeFile, recipeBytes); } @@ -146,7 +138,7 @@ private void writeToFile(File writeFile, byte[] contentBytes) throws IOException writeFile.createNewFile(); Path writeFilePath = writeFile.toPath(); Files.write(writeFilePath, contentBytes); - } catch (IOException e) { + } catch (IOException err) { throw new IOException(ErrorMessages.FILE_WRITE_ERROR_MESSAGE); } } @@ -156,11 +148,11 @@ private void writeToFile(File writeFile, byte[] contentBytes) throws IOException */ public void instantiateDirectoryIfNotExist(String directoryName) throws IOException { File outputDirectory = new File(directoryName); - boolean isSuccessful; if (outputDirectory.exists()) { return; } + boolean isSuccessful; isSuccessful = outputDirectory.mkdir(); if (!isSuccessful) { throw new IOException(ErrorMessages.DIRECTORY_INSTANTIATION_FAILED_MESSAGE + directoryName); From 67c930f1519c54745f4d157ab4b067c4157c9dc2 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 24 Oct 2021 02:37:15 +0800 Subject: [PATCH 311/607] Changed the constant for the datetime file name format to be for output instead, Just realised I intended it to be for output but strangely wrote it as a constant for recipe hmmmmm. --- src/main/java/decodex/storage/Storage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 1f5307919c..4ce3a413e9 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -22,7 +22,7 @@ public class Storage { private static final String DATA_FILE_PREFIX = ".data"; - private static final String RECIPE_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; + private static final String OUTPUT_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; /** * Initializes a new Storage. @@ -98,7 +98,7 @@ public void writeOutputToFile(byte[] outputBytes) throws IOException { instantiateDirectoryIfNotExist(DEFAULT_OUTPUT_DIRECTORY); LocalDateTime currentDateTime = LocalDateTime.now(); - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(RECIPE_FILENAME_FORMAT); + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(OUTPUT_FILENAME_FORMAT); String formattedDateTime = currentDateTime.format(dateTimeFormatter); String newOutputFileName = formattedDateTime + DATA_FILE_PREFIX; From 82bde9d866696b858f2b6b48219421bd67bce329 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 24 Oct 2021 21:31:11 +0800 Subject: [PATCH 312/607] Made some of the suggested changes for the file extensions. --- src/main/java/decodex/storage/Storage.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 4ce3a413e9..f51573ce4d 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -20,7 +20,11 @@ public class Storage { private static final String DEFAULT_OUTPUT_DIRECTORY = "output"; private static final String DEFAULT_RECIPE_DIRECTORY = "recipe"; + /** + * Specifies the corresponding file prefixes. + */ private static final String DATA_FILE_PREFIX = ".data"; + private static final String RECIPE_FILE_PREFIX = ".txt"; private static final String OUTPUT_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; @@ -118,7 +122,7 @@ public void writeOutputToFile(byte[] outputBytes) throws IOException { public void writeRecipeToFile(String recipeName, byte[] recipeBytes) throws IOException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); - String newRecipeFileName = recipeName + DATA_FILE_PREFIX; + String newRecipeFileName = recipeName + RECIPE_FILE_PREFIX; File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); From 0f43363c77285f080290d5a6f6fb9d957c551bfa Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Sun, 24 Oct 2021 23:43:45 +0800 Subject: [PATCH 313/607] Add printing of modules in recipe --- .../java/decodex/commands/SelectCommand.java | 9 +++-- src/main/java/decodex/ui/Ui.java | 37 +++++++++++++++---- .../decodex/ui/messages/RegularMessages.java | 4 +- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index 9eb1ed6607..1577513c99 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -43,10 +43,10 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec switch (selectCategory) { case SELECT_CATEGORY_MODULE: - selectModule(dataManager, moduleManager, ui); + runModule(dataManager, moduleManager, ui); break; case SELECT_CATEGORY_RECIPE: - selectRecipe(dataManager, recipeManager, ui); + runRecipe(dataManager, recipeManager, ui); break; default: throw new CommandException(ErrorMessages.UNKNOWN_SELECTION_CATEGORY); @@ -64,7 +64,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec * @throws ModuleException If the module execution has failed. * @throws DataManagerException If the data is invalid. */ - private void selectModule(DataManager dataManager, ModuleManager moduleManager, Ui ui) + private void runModule(DataManager dataManager, ModuleManager moduleManager, Ui ui) throws CommandException, ModuleManagerException, ModuleException, DataManagerException { if (itemName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); @@ -88,7 +88,7 @@ private void selectModule(DataManager dataManager, ModuleManager moduleManager, * @throws ModuleException If the module execution has failed. * @throws DataManagerException If the data is invalid. */ - private void selectRecipe(DataManager dataManager, RecipeManager recipeManager, Ui ui) + private void runRecipe(DataManager dataManager, RecipeManager recipeManager, Ui ui) throws CommandException, RecipeManagerException, ModuleException, DataManagerException { if (itemName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); @@ -98,6 +98,7 @@ private void selectRecipe(DataManager dataManager, RecipeManager recipeManager, Data newData = recipe.run(dataManager.getCurrentData()); dataManager.setCurrentData(newData); + ui.printRecipeModules(recipe); ui.printOutput(newData.toString()); } } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index cf348c80ad..b704d0903e 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -1,9 +1,12 @@ package decodex.ui; -import decodex.ui.messages.RegularMessages; - +import java.util.ArrayList; import java.util.Scanner; +import decodex.modules.Module; +import decodex.recipes.Recipe; +import decodex.ui.messages.RegularMessages; + /** * The Ui class handles all user input and message output ot the console. */ @@ -91,6 +94,26 @@ public void printRecipeList(String recipeList) { } } + /** + * Prints the modules in a recipe. + * + * @param recipe The provided recipe. + */ + public void printRecipeModules(Recipe recipe) { + if (recipe.getModuleList().size() == 0) { + printSuccess(RegularMessages.RECIPE_EMPTY); + } else { + ArrayList recipeModuleList = recipe.getModuleList(); + String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); + + String message = RegularMessages.RECIPE_MODULES_EXECUTED + + " " + + String.join(", ", recipeModuleNames); + + printSuccess(message); + } + } + /** * Prints the message when a recipe is reset. * @@ -101,8 +124,8 @@ public void printRecipeReset(String recipeName) { assert !recipeName.isBlank() : "Recipe name should not be empty"; printSuccess(String.format(RegularMessages.RESETTED_EDITING_RECIPE, recipeName)); } - - /** + + /** * Prints a message showing that a given module was removed from a given recipe. * * @param moduleName The name of the module removed. @@ -115,7 +138,7 @@ public void printModuleRemovedFromRecipe(String moduleName, String recipeName) { assert !recipeName.isBlank() : "Recipe name should not be empty"; printSuccess(String.format(RegularMessages.REMOVED_RECIPE_FROM_RECIPE + "\n", moduleName, recipeName)); } - + /** * Prints a message showing that a given module was added to a given recipe. * @@ -129,7 +152,7 @@ public void printModuleAddedToRecipe(String moduleName, String recipeName) { assert !recipeName.isBlank() : "Recipe name should not be empty"; printSuccess(String.format(RegularMessages.ADDED_MODULE_TO_RECIPE + "\n", moduleName, recipeName)); } - + /** * Prints a message showing that a recipe was selected. * @@ -173,7 +196,7 @@ public void printRecipeDeleted(String recipeName) { assert !recipeName.isBlank() : "Recipe name should not be empty"; printSuccess(String.format(RegularMessages.RECIPE_DELETED + "\n", recipeName)); } - + /** * Prints a message indicating the creation of a new recipe of given name. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index 807c01c014..f78f3619f5 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -15,5 +15,7 @@ public class RegularMessages { public static final String RESETTED_EDITING_RECIPE = "Removed all modules from recipe %s"; public static final String REMOVED_RECIPE_FROM_RECIPE = "Removed %s module from recipe %s"; public static final String ADDED_MODULE_TO_RECIPE = "Added %s module to recipe %s"; - public static final String RECIPE_SELECT = "Recipe %s selected for editing."; + public static final String RECIPE_SELECT = "Recipe %s selected for editing"; + public static final String RECIPE_EMPTY = "There are no modules in the recipe"; + public static final String RECIPE_MODULES_EXECUTED = "Modules executed:"; } From 19f35fd09fa21f25f676fa9bc70f53321509d5a2 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 25 Oct 2021 00:12:32 +0800 Subject: [PATCH 314/607] Change method execution to guard clause --- src/main/java/decodex/ui/Ui.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index b704d0903e..1237d4f2c8 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -102,16 +102,14 @@ public void printRecipeList(String recipeList) { public void printRecipeModules(Recipe recipe) { if (recipe.getModuleList().size() == 0) { printSuccess(RegularMessages.RECIPE_EMPTY); - } else { - ArrayList recipeModuleList = recipe.getModuleList(); - String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); + return; + } - String message = RegularMessages.RECIPE_MODULES_EXECUTED - + " " - + String.join(", ", recipeModuleNames); + ArrayList recipeModuleList = recipe.getModuleList(); + String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); - printSuccess(message); - } + String message = RegularMessages.RECIPE_MODULES_EXECUTED + " " + String.join(", ", recipeModuleNames); + printSuccess(message); } /** From ea20f3183bc5291d6413d3cd3bcd8833935099c2 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 25 Oct 2021 07:52:10 +0800 Subject: [PATCH 315/607] Add recipe commands to puml --- docs/puml/Style.puml | 5 +- .../DecodeRunSequenceDiagram.puml | 2 +- .../ExecutionOfModuleSequenceDiagram.puml | 53 +++++++++++++++++++ .../ExecutionOfRecipeSequenceDiagram.puml | 52 ++++++++++++++++++ .../commands/ListCommandSequenceDiagram.puml | 39 ++++++++++++++ .../RecipeDeleteCommandSequenceDiagram.puml | 32 +++++++++++ .../RecipeNewCommandSequenceDiagram.puml | 44 +++++++++++++++ .../RecipePopCommandSequenceDiagram.puml | 34 ++++++++++++ .../RecipePushCommandSequenceDiagram.puml | 46 ++++++++++++++++ .../RecipeResetCommandSequenceDiagram.puml | 34 ++++++++++++ .../RecipeSelectCommandSequenceDiagram.puml | 33 ++++++++++++ .../SelectCommandSequenceDiagram.puml | 51 ++++++++---------- 12 files changed, 395 insertions(+), 30 deletions(-) create mode 100644 docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/RecipeDeleteCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/RecipeNewCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/RecipePopCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/RecipePushCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/RecipeResetCommandSequenceDiagram.puml create mode 100644 docs/puml/sequencediagrams/commands/RecipeSelectCommandSequenceDiagram.puml diff --git a/docs/puml/Style.puml b/docs/puml/Style.puml index 99bff12b0d..37571b994d 100644 --- a/docs/puml/Style.puml +++ b/docs/puml/Style.puml @@ -17,4 +17,7 @@ hide circle skinparam DefaultTextAlignment center skinparam Shadowing false -skinparam SequenceGroupBodyBackGroundColor ffffff90 \ No newline at end of file +skinparam SequenceGroupBodyBackGroundColor ffffff90 + +skinparam MinClassWidth 110 +skinparam ParticipantPadding 10 \ No newline at end of file diff --git a/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml b/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml index 6d2100e23e..639f3ba714 100644 --- a/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/DecodeRunSequenceDiagram.puml @@ -39,7 +39,7 @@ loop command is not instance of ExitCommand Decodex -> XYZCommand : run(:DataManager, :ModuleManager, :RecipeManager, :Ui) activate XYZCommand - ref over XYZCommand : Command execution + ref over XYZCommand : Command execution logic XYZCommand --> Decodex deactivate XYZCommand diff --git a/docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml new file mode 100644 index 0000000000..64829184fe --- /dev/null +++ b/docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml @@ -0,0 +1,53 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":SelectCommand" as SelectCommand +end box +box MODULE_COLOR +Participant ":ModuleManager" as ModuleManager +Participant ":Module" as Module +end box +box DATA_COLOR +Participant ":DataManager" as DataManager +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> SelectCommand: runModule() +activate SelectCommand + +alt itemName is blank + <-- SelectCommand: CommandException +else itemName is not blank + SelectCommand -> ModuleManager: selectModule(:String, :String[]) + activate ModuleManager + SelectCommand <-- ModuleManager: :Module + deactivate ModuleManager + + SelectCommand -> DataManager: getCurrentData() + activate DataManager + SelectCommand <-- DataManager: :Data + deactivate DataManager + + SelectCommand -> Module: run(:Data) + activate Module + SelectCommand <-- Module: :Data + deactivate Module + + SelectCommand -> DataManager: setCurrentData(:Data) + activate DataManager + SelectCommand <-- DataManager + deactivate DataManager + + SelectCommand -> Ui: printOutput(:String) + activate Ui + SelectCommand <-- Ui + deactivate Ui + SelectCommand <-[hidden]- Ui + destroy Module + <-- SelectCommand +end + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml new file mode 100644 index 0000000000..70fe0c6383 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml @@ -0,0 +1,52 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":SelectCommand" as SelectCommand +end box +box DATA_COLOR +Participant ":DataManager" as DataManager +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +Participant ":Recipe" as Recipe +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> SelectCommand: runModule() +activate SelectCommand + +alt itemName is blank + <-- SelectCommand: CommandException +else itemName is not blank + SelectCommand -> RecipeManager: getRecipe(:String) + activate RecipeManager + SelectCommand <-- RecipeManager: :Recipe + deactivate RecipeManager + + SelectCommand -> DataManager: getCurrentData() + activate DataManager + SelectCommand <-- DataManager: :DataManager + deactivate DataManager + + SelectCommand -> Recipe: run(:Data) + activate Recipe + SelectCommand <-- Recipe: :Data + deactivate Recipe + + SelectCommand -> DataManager: setCurrentData(:Data) + activate DataManager + SelectCommand <-- DataManager + deactivate DataManager + + SelectCommand -> Ui: printRecipeModules(:Recipe) + activate Ui + SelectCommand <-- Ui + deactivate Ui + + <-- SelectCommand +end + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml new file mode 100644 index 0000000000..dc8eef5b7c --- /dev/null +++ b/docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml @@ -0,0 +1,39 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":ListCommand" as ListCommand +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> ListCommand: run() +activate ListCommand +alt isPrintModuleList + ListCommand -> ListCommand: getModuleList(:ModuleManager) + activate ListCommand + ListCommand --> ListCommand + deactivate ListCommand + ListCommand -> Ui: printModuleList(:String) + activate Ui + ListCommand <-- Ui + deactivate Ui + <-- ListCommand +else isPrintRecipeList + ListCommand -> ListCommand: getRecipeList(:RecipeManager) + activate ListCommand + ListCommand --> ListCommand + deactivate ListCommand + ListCommand -> Ui: printRecipeList(:String) + activate Ui + ListCommand <-- Ui + deactivate Ui + <-- ListCommand +else not isPrintModuleList and not PrintRecipeList + <-- ListCommand: CommandException +end + +deactivate ListCommand + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/RecipeDeleteCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipeDeleteCommandSequenceDiagram.puml new file mode 100644 index 0000000000..2973af8c3d --- /dev/null +++ b/docs/puml/sequencediagrams/commands/RecipeDeleteCommandSequenceDiagram.puml @@ -0,0 +1,32 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":RecipeDeleteCommand" as RecipeDeleteCommand +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> RecipeDeleteCommand: run() +activate RecipeDeleteCommand + +alt recipeName is blank + <-- RecipeDeleteCommand: CommandException +else recipeName is not blank + RecipeDeleteCommand -> RecipeManager: removeRecipe(:String) + activate RecipeManager + RecipeDeleteCommand <-- RecipeManager + deactivate RecipeManager + + RecipeDeleteCommand -> Ui: printRecipeDeleted(:String) + activate Ui + RecipeDeleteCommand <-- Ui + deactivate Ui + <-- RecipeDeleteCommand +end + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/RecipeNewCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipeNewCommandSequenceDiagram.puml new file mode 100644 index 0000000000..ead63996d4 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/RecipeNewCommandSequenceDiagram.puml @@ -0,0 +1,44 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":RecipeNewCommand" as RecipeNewCommand +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +Participant ":Recipe" as Recipe +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> RecipeNewCommand: run() +activate RecipeNewCommand + +alt recipeName is blank + <-- RecipeNewCommand: CommandException +else recipeName is not blank + create Recipe + RecipeNewCommand -> Recipe: new Recipe(:String) + activate Recipe + RecipeNewCommand <-- Recipe + deactivate Recipe + + RecipeNewCommand -> RecipeManager: addRecipe(:Recipe) + activate RecipeManager + RecipeNewCommand <-- RecipeManager + deactivate RecipeManager + + RecipeNewCommand -> RecipeManager: selectRecipeForEditing(:String) + activate RecipeManager + RecipeNewCommand <-- RecipeManager + deactivate RecipeManager + + RecipeNewCommand -> Ui: printNewRecipeCreated(:String) + activate Ui + RecipeNewCommand <-- Ui + deactivate Ui + <-- RecipeNewCommand +end + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/RecipePopCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipePopCommandSequenceDiagram.puml new file mode 100644 index 0000000000..5849a360c2 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/RecipePopCommandSequenceDiagram.puml @@ -0,0 +1,34 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":RecipePopCommand" as RecipePopCommand +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> RecipePopCommand: run() +activate RecipePopCommand + +RecipePopCommand -> RecipeManager: popModuleFromEditedRecipe() +activate RecipeManager +RecipePopCommand <-- RecipeManager: :Module +deactivate RecipeManager + +RecipePopCommand -> RecipeManager: getEditingRecipe() +activate RecipeManager +RecipePopCommand <-- RecipeManager: :Recipe +deactivate RecipeManager + +RecipePopCommand -> Ui: printModuleRemovedFromRecipe(:String, :String) +activate Ui +RecipePopCommand <-- Ui +deactivate Ui + +<-- RecipePopCommand + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/RecipePushCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipePushCommandSequenceDiagram.puml new file mode 100644 index 0000000000..7a486dca41 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/RecipePushCommandSequenceDiagram.puml @@ -0,0 +1,46 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":RecipePushCommand" as RecipePushCommand +end box +box MODULE_COLOR +Participant ":ModuleManager" as ModuleManager +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> RecipePushCommand: run() +activate RecipePushCommand + +alt moduleName is blank + <-- RecipePushCommand: CommandException +else moduleName is not blank + RecipePushCommand -> ModuleManager: selectModule(:String, :String[]) + activate ModuleManager + RecipePushCommand <-- ModuleManager: :Module + deactivate ModuleManager + + RecipePushCommand -> RecipeManager: getEditingRecipe() + activate RecipeManager + RecipePushCommand <-- RecipeManager: :Recipe + deactivate RecipeManager + + RecipePushCommand -> RecipeManager: pushModuleIntoEditedRecipe(:Module) + activate RecipeManager + RecipePushCommand <-- RecipeManager + deactivate RecipeManager + + RecipePushCommand -> Ui: printModuleAddedToRecipe(:String, :String) + activate Ui + RecipePushCommand <-- Ui + deactivate Ui + + <-- RecipePushCommand +end + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/RecipeResetCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipeResetCommandSequenceDiagram.puml new file mode 100644 index 0000000000..98f49850ed --- /dev/null +++ b/docs/puml/sequencediagrams/commands/RecipeResetCommandSequenceDiagram.puml @@ -0,0 +1,34 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":RecipeResetCommand" as RecipeResetCommand +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> RecipeResetCommand: run() +activate RecipeResetCommand + +RecipeResetCommand -> RecipeManager: getEditingRecipe() +activate RecipeManager +RecipeResetCommand <-- RecipeManager: :Recipe +deactivate RecipeManager + +RecipeResetCommand -> RecipeManager: resetEditedRecipe() +activate RecipeManager +RecipeResetCommand <-- RecipeManager +deactivate RecipeManager + +RecipeResetCommand -> Ui: printRecipeReset(:String) +activate Ui +RecipeResetCommand <-- Ui +deactivate Ui + +<-- RecipeResetCommand + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/RecipeSelectCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipeSelectCommandSequenceDiagram.puml new file mode 100644 index 0000000000..8d1e40e355 --- /dev/null +++ b/docs/puml/sequencediagrams/commands/RecipeSelectCommandSequenceDiagram.puml @@ -0,0 +1,33 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":RecipeSelectCommand" as RecipeSelectCommand +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> RecipeSelectCommand: run() +activate RecipeSelectCommand + +alt recipeName is blank + <-- RecipeSelectCommand: CommandException +else recipeName is not blank + RecipeSelectCommand -> RecipeManager: selectRecipeForEditing(:String) + activate RecipeManager + RecipeSelectCommand <-- RecipeManager + deactivate RecipeManager + + RecipeSelectCommand -> Ui: printRecipeSelected(:String) + activate Ui + RecipeSelectCommand <-- Ui + deactivate Ui + + <-- RecipeSelectCommand +end + +@enduml \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml index bf6eb4db0f..d48b11c906 100644 --- a/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/SelectCommandSequenceDiagram.puml @@ -14,40 +14,35 @@ end box box UI_COLOR Participant ":Ui" as Ui end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +Participant ":Recipe" as Recipe +end box -> SelectCommand: run() activate SelectCommand -alt moduleName is not blank - SelectCommand -> ModuleManager: selectModule(moduleName: String, parameters: String[]) - activate ModuleManager - SelectCommand <-- ModuleManager: module: Module - deactivate ModuleManager - - SelectCommand -> DataManager: getCurrentData() - activate DataManager - SelectCommand <-- DataManager: :Data - deactivate DataManager - - SelectCommand -> Module: run(data: Data) - activate Module - SelectCommand <-- Module: newData: Data - deactivate Module - - SelectCommand -> DataManager: setCurrentData(currentData: Data) - activate DataManager - SelectCommand <-- DataManager - deactivate DataManager - - SelectCommand -> Ui: printOutput(newData) - activate Ui - SelectCommand <-- Ui - deactivate Ui - +alt selectCategory = "module" + SelectCommand -> SelectCommand: runModule() + activate SelectCommand + ref over SelectCommand, ModuleManager, Module, DataManager, Ui + Execution of module + end + SelectCommand --> SelectCommand + deactivate SelectCommand <-- SelectCommand -else moduleName is blank - <-- SelectCommand: CommandException +else selectCategory = "recipe" + SelectCommand -> SelectCommand: runRecipe() + activate SelectCommand + ref over SelectCommand, RecipeManager, Recipe, ModuleManager, Module, DataManager, Ui + Execution of recipe + end + + SelectCommand --> SelectCommand deactivate SelectCommand + <-- SelectCommand +else unknown selectCategory + <-- SelectCommand: CommandException end @enduml \ No newline at end of file From aee966278060cb55e6ea59b4558ed8b790d613f1 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 25 Oct 2021 16:00:42 +0800 Subject: [PATCH 316/607] Added methods for loading saved recipes from recipe directory. --- .gitignore | 3 + src/main/java/decodex/Decodex.java | 17 +++- src/main/java/decodex/storage/Storage.java | 100 ++++++++++++++++++++- 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f69985ef1f..04563a1a3f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,8 @@ src/main/resources/docs/ *.iml bin/ +# Recipe files +/recipe/* + /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index d0e85db245..934b160dc9 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -12,8 +12,12 @@ import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.parser.Parser; +import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; + +import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -30,6 +34,7 @@ public class Decodex { private static RecipeManager recipeManager; private static Parser parser; private static Ui ui; + private static Storage storage; public Decodex() { initDecodex(); @@ -42,9 +47,15 @@ private void initDecodex() { logger.setLevel(Level.INFO); dataManager = new DataManager(); moduleManager = new ModuleManager(); - recipeManager = new RecipeManager(); parser = new Parser(); ui = new Ui(); + storage = new Storage(moduleManager); + try { + Recipe[] savedRecipes = loadSavedRecipes(); + } catch (IOException | ModuleException | ModuleManagerException err) { + ui.printError(err); + } + recipeManager = new RecipeManager(); } /** @@ -73,4 +84,8 @@ public void run() { } } while (!(command instanceof ExitCommand)); } + + public Recipe[] loadSavedRecipes() throws IOException, ModuleException, ModuleManagerException { + return storage.loadRecipesFromDirectory(moduleManager); + } } diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index f51573ce4d..ef23334b01 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -1,5 +1,10 @@ package decodex.storage; +import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; +import decodex.modules.ModuleManager; +import decodex.modules.Module; +import decodex.recipes.Recipe; import decodex.ui.messages.ErrorMessages; import java.io.File; @@ -9,6 +14,8 @@ import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; // @@author Kair0s3 public class Storage { @@ -30,8 +37,99 @@ public class Storage { /** * Initializes a new Storage. + * + * @param moduleManager + */ + public Storage(ModuleManager moduleManager) { + } + + /** + * Loads all the recipes previously saved in the recipe directory. + * + * @param moduleManager The ModuleManager object. + * @return The list of Recipe objects. + * @throws IOException If something went wrong when reading the file. + * @throws ModuleException If module parameters are invalid. + * @throws ModuleManagerException If provided module name is not an available module. + */ + public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager) + throws IOException, ModuleException, ModuleManagerException { + instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); + File[] recipeFiles = getAllRecipeFiles(); + + ArrayList tempRecipeList = new ArrayList<>(); + for (File recipeFile : recipeFiles) { + Recipe recipe = readRecipeFromFile(recipeFile.getName(), recipeFile, moduleManager); + tempRecipeList.add(recipe); + } + + int tempRecipeListSize = tempRecipeList.size(); + Recipe[] recipeList = tempRecipeList.toArray(new Recipe[tempRecipeListSize]); + return recipeList; + } + + /** + * Gets all the recipe files in the recipe directory. + * + * @return The list of recipe File objects. + */ + private File[] getAllRecipeFiles() { + File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); + File[] files = recipeDirectory.listFiles(); + File[] recipeFiles = Arrays.stream(files) + .filter(file -> file.isFile()) + .toArray(size -> new File[size]); + + return recipeFiles; + } + + /** + * Parses the saved recipe content and returns the Recipe. + * + * @param recipeFileName The name of the recipe file. + * @param recipeContent The contents of the recipe file. + * @param moduleManager The ModuleManger object + * @return The Recipe object. + * @throws ModuleException If module parameters are invalid. + * @throws ModuleManagerException If provided module name is not an available module. + */ + private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, ModuleManager moduleManager) + throws ModuleException, ModuleManagerException { + Recipe recipe = new Recipe(recipeFileName); + String[] recipeLines = recipeContent.split("\\r?\\n"); + + for (String recipeLine : recipeLines) { + if (recipeLine.isEmpty()) { + continue; + } + String[] tokens = recipeLine.split(" "); + String moduleName = tokens[0]; + String[] moduleParameters = Arrays.copyOfRange(tokens, 1, tokens.length); + Module module = moduleManager.selectModule(moduleName, moduleParameters); + recipe.push(module); + } + return recipe; + } + + /** + * Reads the recipe file contents and returns the Recipe. + * + * @param recipeFilename The name of the recipe. + * @param recipeFile The recipe File object. + * @param moduleManager The ModuleManager object. + * @return The Recipe object. + * @throws IOException If something went wrong when reading the file. + * @throws ModuleException If module parameters are invalid. + * @throws ModuleManagerException If provided module name is not an available module. */ - public Storage() { + public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleManager moduleManager) + throws IOException, ModuleException, ModuleManagerException { + Path recipeFilePath = recipeFile.toPath(); + + byte[] recipeContentBytes = readFromFile(recipeFilePath); + String recipeContent = new String(recipeContentBytes); + Recipe loadedRecipe = parseContentToRecipe(recipeFilename, recipeContent, moduleManager); + return loadedRecipe; } /** From f522ccbfdb9ef10584d90d704c3bce52d93ac10b Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 25 Oct 2021 16:54:39 +0800 Subject: [PATCH 317/607] Add restriction to recipe name in `setName` * Remove some unused imports from some files * Update methods and tests that creates recipes to throw RecipeExcepton --- .../decodex/commands/RecipeNewCommand.java | 3 +- src/main/java/decodex/recipes/Recipe.java | 20 ++++++----- .../decodex/ui/messages/ErrorMessages.java | 2 ++ .../commands/RecipeDeleteCommandTest.java | 2 +- .../commands/RecipePopCommandTest.java | 2 +- .../commands/RecipePushCommandTest.java | 6 ++-- .../commands/RecipeResetCommandTest.java | 6 ++-- .../commands/RecipeSelectCommandTest.java | 5 ++- .../decodex/recipes/RecipeManagerTest.java | 22 ++++++------ src/test/java/decodex/recipes/RecipeTest.java | 34 ++++++++++++++----- 10 files changed, 66 insertions(+), 36 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeNewCommand.java b/src/main/java/decodex/commands/RecipeNewCommand.java index a40bc85a00..3771ccdd81 100644 --- a/src/main/java/decodex/commands/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/RecipeNewCommand.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; @@ -23,7 +24,7 @@ public RecipeNewCommand(String recipeName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, ModuleException, RecipeManagerException { + throws CommandException, ModuleException, RecipeException, RecipeManagerException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index 56eb5f5c7a..71d6c76bcf 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -1,17 +1,13 @@ package decodex.recipes; -import decodex.data.Data; -import decodex.data.DataManager; -import decodex.data.exception.DataManagerException; -import decodex.data.exception.ModuleException; -import decodex.ui.Ui; import java.util.ArrayList; import java.util.logging.Logger; +import java.util.regex.Pattern; import decodex.Decodex; +import decodex.data.exception.RecipeException; import decodex.data.Data; import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeException; import decodex.modules.Module; import decodex.ui.messages.ErrorMessages; @@ -20,6 +16,8 @@ */ public class Recipe { + private static final String VALID_NAME_REGEX = "^[a-zA-Z0-9_]+$"; + private Logger logger = Decodex.logger; private String name; @@ -30,17 +28,21 @@ public class Recipe { * * @param name The name of the recipe object to be created. */ - public Recipe(String name) { + public Recipe(String name) throws RecipeException { moduleList = new ArrayList<>(); - this.name = name; + setName(name); } public String getName() { return name; } - public void setName(String name) { + public void setName(String name) throws RecipeException { assert name != null : "Recipe name should not be null"; + boolean isValidRecipeName = Pattern.matches(VALID_NAME_REGEX, name); + if (!isValidRecipeName) { + throw new RecipeException(ErrorMessages.INVALID_RECIPE_NAME); + } this.name = name; } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 81b6496e4b..73bdde6aa2 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -13,6 +13,8 @@ public class ErrorMessages { public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; public static final String EMPTY_RECIPE_MESSAGE = "The recipe is currently empty. There is no module to be " + "removed."; + public static final String INVALID_RECIPE_NAME = "Recipe names can contain only alphabets, numbers and " + + "underscores and must be non-empty."; public static final String DUPLICATE_RECIPE_NAME_MESSAGE = "A recipe with the given name already exists."; public static final String RECIPE_NOT_FOUND_MESSAGE = "The given recipe could not be found."; public static final String EDITING_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected for editing."; diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java index bc9707f9be..64dc13f4c3 100644 --- a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java @@ -36,7 +36,7 @@ void run_deleteValidRecipe_recipeDeleted() throws CommandException, RecipeExcept } @Test - void run_deleteInvalidRecipe_expectException() throws RecipeManagerException { + void run_deleteInvalidRecipe_expectException() throws RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/commands/RecipePopCommandTest.java index bf7e17abee..4262406852 100644 --- a/src/test/java/decodex/commands/RecipePopCommandTest.java +++ b/src/test/java/decodex/commands/RecipePopCommandTest.java @@ -43,7 +43,7 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsZero() } @Test - public void run_emptyEditingRecipe_expectException() throws RecipeManagerException { + public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java index 9ab593899d..83d2aaf417 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.exception.RecipeException; import org.junit.jupiter.api.Test; import decodex.data.DataManager; @@ -18,7 +20,7 @@ class RecipePushCommandTest { @Test public void run_oneModuleToEditingRecipe_recipeSizeIsOne() - throws RecipeManagerException, CommandException, ModuleException, ModuleManagerException { + throws RecipeException, RecipeManagerException, CommandException, ModuleException, ModuleManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); @@ -39,7 +41,7 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsOne() } @Test - public void run_emptyModuleName_expectException() throws RecipeManagerException { + public void run_emptyModuleName_expectException() throws RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/commands/RecipeResetCommandTest.java index a99aa15df0..4e63992dce 100644 --- a/src/test/java/decodex/commands/RecipeResetCommandTest.java +++ b/src/test/java/decodex/commands/RecipeResetCommandTest.java @@ -1,6 +1,8 @@ package decodex.commands; import static org.junit.jupiter.api.Assertions.assertEquals; + +import decodex.data.exception.RecipeException; import org.junit.jupiter.api.Test; import decodex.data.DataManager; @@ -16,8 +18,8 @@ class RecipeResetCommandTest { @Test - public void run_oneModuleInEditingRecipe_recipeSizeIsZero() - throws RecipeManagerException, ModuleManagerException, ModuleException { + public void run_oneModuleInEditingRecipe_recipeSizeIsZero() throws RecipeException, RecipeManagerException, + ModuleManagerException, ModuleException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "test"; Recipe testRecipe = new Recipe(testRecipeName); diff --git a/src/test/java/decodex/commands/RecipeSelectCommandTest.java b/src/test/java/decodex/commands/RecipeSelectCommandTest.java index 3bbac451d0..2ba4071f15 100644 --- a/src/test/java/decodex/commands/RecipeSelectCommandTest.java +++ b/src/test/java/decodex/commands/RecipeSelectCommandTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.exception.RecipeException; import org.junit.jupiter.api.Test; import decodex.data.DataManager; @@ -15,7 +17,8 @@ class RecipeSelectCommandTest { @Test - public void run_selectExistingRecipe_existingRecipe() throws RecipeManagerException, CommandException { + public void run_selectExistingRecipe_existingRecipe() throws RecipeException, RecipeManagerException, + CommandException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index ab6e0e90c4..4fcf7d6e0f 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -13,7 +13,7 @@ class RecipeManagerTest { @Test - void addRecipe_repeatedRecipeName_expectException() throws RecipeManagerException { + void addRecipe_repeatedRecipeName_expectException() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); Recipe newRecipe = new Recipe("sameRecipeName"); recipeManager.addRecipe(newRecipe); @@ -23,7 +23,7 @@ void addRecipe_repeatedRecipeName_expectException() throws RecipeManagerExceptio } @Test - void removeRecipe_oneRecipe_removedRecipeIsTheOneRecipe() throws RecipeManagerException { + void removeRecipe_oneRecipe_removedRecipeIsTheOneRecipe() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -40,7 +40,7 @@ void removeRecipe_noRecipe_expectException() { } @Test - void removeRecipe_recipeIsEditedRecipe_expectException() throws RecipeManagerException { + void removeRecipe_recipeIsEditedRecipe_expectException() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -52,7 +52,7 @@ void removeRecipe_recipeIsEditedRecipe_expectException() throws RecipeManagerExc } @Test - void getRecipe_oneRecipe_managerContainOneRecipe() throws RecipeManagerException { + void getRecipe_oneRecipe_managerContainOneRecipe() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -70,7 +70,7 @@ void getRecipe_noRecipe_expectException() { } @Test - void getEditedRecipe_oneEditedRecipe_editedRecipe() throws RecipeManagerException { + void getEditedRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -89,7 +89,7 @@ void getEditedRecipe_noEditedRecipe_expectException() { } @Test - void selectRecipe_oneEditedRecipe_editedRecipe() throws RecipeManagerException { + void selectRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -108,7 +108,7 @@ void selectRecipe_nonExistentRecipeName_expectException() { } @Test - void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() throws RecipeManagerException { + void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -123,7 +123,7 @@ void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() throws RecipeManage } @Test - void pushModuleIntoEditedRecipe_noEditedRecipe_expectException() throws RecipeManagerException { + void pushModuleIntoEditedRecipe_noEditedRecipe_expectException() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -149,7 +149,7 @@ void popModuleFromEditedRecipe_oneEditedRecipe_poppedModule() throws RecipeExcep } @Test - void popModuleFromEditedRecipe_noEditedRecipe_expectException() throws RecipeManagerException { + void popModuleFromEditedRecipe_noEditedRecipe_expectException() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -161,7 +161,7 @@ void popModuleFromEditedRecipe_noEditedRecipe_expectException() throws RecipeMan } @Test - void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeManagerException { + void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); @@ -174,7 +174,7 @@ void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeManagerException { } @Test - void resetEditedRecipe_noEditedRecipe_expectException() throws RecipeManagerException { + void resetEditedRecipe_noEditedRecipe_expectException() throws RecipeException, RecipeManagerException { RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index 3a1ca295e7..5030b4eff3 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -1,11 +1,12 @@ package decodex.recipes; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; import decodex.data.Data; -import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.modules.base64.Base64Decoder; @@ -15,13 +16,30 @@ import decodex.modules.hex.HexEncoder; import decodex.modules.Module; import decodex.modules.rot.RotEncoder; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; class RecipeTest { @Test - void push_singleModule_recipeContainsOneModule() { + void recipe_allowedCharactersInName_recipeCreated() throws RecipeException { + String validRecipeName = "test_Recipe"; + Recipe testRecipe = new Recipe(validRecipeName); + assertNotNull(testRecipe); + } + + @Test + void recipe_emptyRecipeName_recipeCreated() { + String emptyRecipeName = ""; + assertThrows(RecipeException.class, () -> new Recipe(emptyRecipeName)); + } + + @Test + void recipe_illegalCharactersInName_recipeCreated() { + String invalidRecipeName = "**bad-name?!"; + assertThrows(RecipeException.class, () -> new Recipe(invalidRecipeName)); + } + + @Test + void push_singleModule_recipeContainsOneModule() throws RecipeException { Recipe recipe = new Recipe("testRecipe"); Module module = new HexEncoder(); recipe.push(module); @@ -41,14 +59,14 @@ void pop_singleModule_latestModule() throws RecipeException { } @Test - void pop_emptyRecipe_expectException() { + void pop_emptyRecipe_expectException() throws RecipeException { Recipe recipe = new Recipe("testRecipe"); assertThrows(RecipeException.class, () -> recipe.pop()); } @Test - void reset_emptyModuleList() { + void reset_emptyModuleList() throws RecipeException { Recipe recipe = new Recipe("testRecipe"); recipe.push(new HexEncoder()); recipe.push(new HexDecoder()); @@ -61,7 +79,7 @@ void reset_emptyModuleList() { } @Test - void run_chainOfModules_encodedOutput() throws ModuleException { + void run_chainOfModules_encodedOutput() throws RecipeException, ModuleException { Recipe recipe = new Recipe("rainbowTest"); recipe.push(new HexEncoder()); @@ -79,7 +97,7 @@ void run_chainOfModules_encodedOutput() throws ModuleException { } @Test() - void run_chainOfModulesFailHalfway_expectException() { + void run_chainOfModulesFailHalfway_expectException() throws RecipeException { Recipe recipe = new Recipe("rainbowTest"); recipe.push(new HexEncoder()); From d86965a2674992e9326c4d012fca1b42dbf831d6 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 25 Oct 2021 21:36:36 +0800 Subject: [PATCH 318/607] Added additional check to return null if there is no recipe files. --- src/main/java/decodex/storage/Storage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index ef23334b01..b81c168615 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -57,6 +57,10 @@ public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager) instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); File[] recipeFiles = getAllRecipeFiles(); + if (recipeFiles.length == 0) { + return null; + } + ArrayList tempRecipeList = new ArrayList<>(); for (File recipeFile : recipeFiles) { Recipe recipe = readRecipeFromFile(recipeFile.getName(), recipeFile, moduleManager); From c7ac9c5050cdfd867a75a2008b8e3d5813f04403 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:10:39 +0800 Subject: [PATCH 319/607] Add `RecipeCommandParser` --- .../{ => recipe}/RecipeDeleteCommand.java | 3 +- .../{ => recipe}/RecipeNewCommand.java | 3 +- .../{ => recipe}/RecipePopCommand.java | 3 +- .../{ => recipe}/RecipePushCommand.java | 3 +- .../{ => recipe}/RecipeResetCommand.java | 3 +- .../{ => recipe}/RecipeSelectCommand.java | 3 +- src/main/java/decodex/parser/Parser.java | 58 ++---- .../decodex/parser/RecipeCommandParser.java | 165 ++++++++++++++++++ .../decodex/ui/messages/ErrorMessages.java | 1 + .../commands/RecipeDeleteCommandTest.java | 1 + .../commands/RecipeNewCommandTest.java | 1 + .../commands/RecipePopCommandTest.java | 1 + .../commands/RecipePushCommandTest.java | 1 + .../commands/RecipeResetCommandTest.java | 1 + .../commands/RecipeSelectCommandTest.java | 1 + .../parser/RecipeCommandParserTest.java | 130 ++++++++++++++ 16 files changed, 333 insertions(+), 45 deletions(-) rename src/main/java/decodex/commands/{ => recipe}/RecipeDeleteCommand.java (93%) rename src/main/java/decodex/commands/{ => recipe}/RecipeNewCommand.java (94%) rename src/main/java/decodex/commands/{ => recipe}/RecipePopCommand.java (90%) rename src/main/java/decodex/commands/{ => recipe}/RecipePushCommand.java (93%) rename src/main/java/decodex/commands/{ => recipe}/RecipeResetCommand.java (88%) rename src/main/java/decodex/commands/{ => recipe}/RecipeSelectCommand.java (90%) create mode 100644 src/main/java/decodex/parser/RecipeCommandParser.java create mode 100644 src/test/java/decodex/parser/RecipeCommandParserTest.java diff --git a/src/main/java/decodex/commands/RecipeDeleteCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java similarity index 93% rename from src/main/java/decodex/commands/RecipeDeleteCommand.java rename to src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java index 994628d691..8b0261eed0 100644 --- a/src/main/java/decodex/commands/RecipeDeleteCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java @@ -1,5 +1,6 @@ -package decodex.commands; +package decodex.commands.recipe; +import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/main/java/decodex/commands/RecipeNewCommand.java b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java similarity index 94% rename from src/main/java/decodex/commands/RecipeNewCommand.java rename to src/main/java/decodex/commands/recipe/RecipeNewCommand.java index a40bc85a00..8a05a8d31b 100644 --- a/src/main/java/decodex/commands/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java @@ -1,5 +1,6 @@ -package decodex.commands; +package decodex.commands.recipe; +import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/main/java/decodex/commands/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java similarity index 90% rename from src/main/java/decodex/commands/RecipePopCommand.java rename to src/main/java/decodex/commands/recipe/RecipePopCommand.java index f080404ff3..627a318a26 100644 --- a/src/main/java/decodex/commands/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -1,5 +1,6 @@ -package decodex.commands; +package decodex.commands.recipe; +import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; diff --git a/src/main/java/decodex/commands/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java similarity index 93% rename from src/main/java/decodex/commands/RecipePushCommand.java rename to src/main/java/decodex/commands/recipe/RecipePushCommand.java index 4136a89a2c..49eff97cc8 100644 --- a/src/main/java/decodex/commands/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -1,5 +1,6 @@ -package decodex.commands; +package decodex.commands.recipe; +import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/main/java/decodex/commands/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java similarity index 88% rename from src/main/java/decodex/commands/RecipeResetCommand.java rename to src/main/java/decodex/commands/recipe/RecipeResetCommand.java index 09f6426f02..2d91f3d189 100644 --- a/src/main/java/decodex/commands/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -1,5 +1,6 @@ -package decodex.commands; +package decodex.commands.recipe; +import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; diff --git a/src/main/java/decodex/commands/RecipeSelectCommand.java b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java similarity index 90% rename from src/main/java/decodex/commands/RecipeSelectCommand.java rename to src/main/java/decodex/commands/recipe/RecipeSelectCommand.java index d07af2af09..ac28cbdfd8 100644 --- a/src/main/java/decodex/commands/RecipeSelectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java @@ -1,5 +1,6 @@ -package decodex.commands; +package decodex.commands.recipe; +import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.RecipeManagerException; diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 6c600eee18..6cfb4ec913 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -21,18 +21,12 @@ public class Parser { * Specifies the index of the tokens where specific parameters can be found. */ private static final int COMMAND_INDEX = 0; - private static final int MODULE_NAME_INDEX_IN_TOKENS = 0; /** * Specifies the starting index where the arguments can be found. */ private static final int STARTING_ARGUMENTS_INDEX = 1; - /** - * Specifies the starting index where the module parameters can be found. - */ - private static final int MODULE_PARAMETERS_INDEX = 2; - /** * Specifies the token used to split the user input by. */ @@ -58,6 +52,9 @@ public class Parser { private static final int SELECT_ITEM_NAME_INDEX = 2; private static final int SELECT_PARAMETERS_START_INDEX = 3; + private static final String RECIPE_COMMAND_WORD = "recipe"; + private static final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); + /** * Returns the type of command that the user has specified. * @@ -84,39 +81,6 @@ public String getCommandType(String userInput) throws ParserException { return commandType; } - /** - * Returns the name of the module that the user has specified. - * - * @param userInput The input specified by the user. - * @return The name of the module. - * @throws ParserException If the user input is missing the module name. - */ - public String getModuleName(String userInput) throws ParserException { - String strippedUserInput = userInput.stripLeading(); - String[] tokens = strippedUserInput.split(" ", -1); - - String[] argumentTokens = Arrays.stream(Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length)) - .filter(value -> !value.isBlank()) - .toArray(size -> new String[size]); - if (argumentTokens.length == 0) { - throw new ParserException(ErrorMessages.MISSING_MODULE_NAME); - } - return argumentTokens[MODULE_NAME_INDEX_IN_TOKENS]; - } - - /** - * Returns the module parameters that the user has specified. - * - * @param userInput The input specified by the user. - * @return A String array of module parameters. - */ - public String[] getModuleParameters(String userInput) { - String strippedUserInput = userInput.stripLeading(); - String[] tokens = strippedUserInput.split(SPLIT_REGEX); - String[] parameters = Arrays.copyOfRange(tokens, MODULE_PARAMETERS_INDEX, tokens.length); - return parameters; - } - /** * Returns the InputCommand's argument string that the user has specified. * @@ -164,6 +128,9 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc case SelectCommand.COMMAND_WORD: command = prepareSelectCommand(userInput); break; + case RECIPE_COMMAND_WORD: + command = prepareRecipeSubcommands(userInput); + break; default: throw new ParserException(ErrorMessages.UNKNOWN_COMMAND); } @@ -263,4 +230,17 @@ private SelectCommand prepareSelectCommand(String userInput) throws CommandExcep return new SelectCommand(selectCategory, itemName, parameters); } + + /** + * Prepares and returns recipe subcommands using the specified user input. + * + * @param userInput The user input specified by the user. + * @return The respective recipe command. + * @throws CommandException If the recipe command is invalid. + */ + private Command prepareRecipeSubcommands(String userInput) throws CommandException { + String[] tokens = getTokens(userInput); + String[] recipeCommandArguments = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); + return recipeCommandParser.parseCommand(recipeCommandArguments); + } } diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java new file mode 100644 index 0000000000..2468805c89 --- /dev/null +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -0,0 +1,165 @@ +package decodex.parser; + +import java.util.Arrays; + +import decodex.commands.Command; +import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.commands.recipe.RecipeNewCommand; +import decodex.commands.recipe.RecipePopCommand; +import decodex.commands.recipe.RecipePushCommand; +import decodex.commands.recipe.RecipeResetCommand; +import decodex.commands.recipe.RecipeSelectCommand; +import decodex.data.exception.CommandException; +import decodex.ui.messages.ErrorMessages; + +/** + * The RecipeCommandParser class handles the parsing and validation of recipe commands. + */ +public class RecipeCommandParser { + + /** + * Specifies the token used to split the user input by. + */ + private static final String SPLIT_REGEX = "\\s+"; + + private static final int SUBCOMMAND_WORD_INDEX = 0; + private static final int SUBARGUMENT_STARTING_INDEX = 1; + private static final int MODULE_PARAMETERS_STARTING_INDEX = 2; + private static final int SUBCOMMAND_NO_ARGUMENT_LENGTH = 1; + private static final int SUBCOMMAND_WITH_ARGUMENT_LENGTH = 2; + + public RecipeCommandParser() { + } + + public Command parseCommand(String[] subCommandTokens) throws CommandException { + assert subCommandTokens != null : "subCommandTokens should not be null"; + + // Check minimum subcommand length + if (subCommandTokens.length < SUBCOMMAND_NO_ARGUMENT_LENGTH) { + throw new CommandException(ErrorMessages.MISSING_ARGUMENT); + } + + Command recipeCommand; + + String subCommand = subCommandTokens[SUBCOMMAND_WORD_INDEX]; + switch (subCommand) { + case RecipeNewCommand.COMMAND_WORD: + recipeCommand = prepareRecipeNewCommand(subCommandTokens); + break; + case RecipeSelectCommand.COMMAND_WORD: + recipeCommand = prepareRecipeSelectCommand(subCommandTokens); + break; + case RecipePushCommand.COMMAND_WORD: + recipeCommand = prepareRecipePushCommand(subCommandTokens); + break; + case RecipePopCommand.COMMAND_WORD: + recipeCommand = prepareRecipePopCommand(subCommandTokens); + break; + case RecipeResetCommand.COMMAND_WORD: + recipeCommand = prepareRecipeResetCommand(subCommandTokens); + break; + case RecipeDeleteCommand.COMMAND_WORD: + recipeCommand = prepareRecipeDeleteCommand(subCommandTokens); + break; + default: + throw new CommandException(ErrorMessages.INVALID_RECIPE_COMMAND); + } + + return recipeCommand; + } + + /** + * Checks the number of arguments is valid. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @param numberOfArguments The valid number of arguments. + * @throws CommandException If the number of arguments is not valid. + */ + private void checkValidNumberOfArguments(String[] subCommandTokens, int numberOfArguments) + throws CommandException { + if (subCommandTokens.length != numberOfArguments) { + throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); + } + } + + /** + * Prepares and returns the RecipeNewCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipeNewCommand object. + * @throws CommandException If the number of arguments exceed valid amount. + */ + private RecipeNewCommand prepareRecipeNewCommand(String[] subCommandTokens) throws CommandException { + checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_WITH_ARGUMENT_LENGTH); + String recipeName = subCommandTokens[SUBARGUMENT_STARTING_INDEX]; + return new RecipeNewCommand(recipeName); + } + + /** + * Prepares and returns the RecipeSelectCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipeSelectCommand object. + * @throws CommandException If the number of arguments exceed valid amount. + */ + private RecipeSelectCommand prepareRecipeSelectCommand(String[] subCommandTokens) throws CommandException { + checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_WITH_ARGUMENT_LENGTH); + String recipeName = subCommandTokens[SUBARGUMENT_STARTING_INDEX]; + return new RecipeSelectCommand(recipeName); + } + + /** + * Prepares and returns the RecipePushCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipePushCommand object. + * @throws CommandException If the number of arguments exceed valid amount. + */ + private RecipePushCommand prepareRecipePushCommand(String[] subCommandTokens) throws CommandException { + if (subCommandTokens.length < SUBCOMMAND_WITH_ARGUMENT_LENGTH) { + throw new CommandException(ErrorMessages.MISSING_ARGUMENT); + } + + String moduleName = subCommandTokens[SUBARGUMENT_STARTING_INDEX]; + String[] parameters = Arrays.copyOfRange(subCommandTokens, MODULE_PARAMETERS_STARTING_INDEX, + subCommandTokens.length); + return new RecipePushCommand(moduleName, parameters); + } + + /** + * Prepares and returns the RecipePopCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipePopCommand object. + * @throws CommandException If the number of arguments exceed valid amount. + */ + private RecipePopCommand prepareRecipePopCommand(String[] subCommandTokens) throws CommandException { + checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_NO_ARGUMENT_LENGTH); + return new RecipePopCommand(); + } + + /** + * Prepares and returns the RecipeResetCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipeResetCommand object. + * @throws CommandException If the number of arguments exceed valid amount. + */ + private RecipeResetCommand prepareRecipeResetCommand(String[] subCommandTokens) throws CommandException { + checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_NO_ARGUMENT_LENGTH); + return new RecipeResetCommand(); + } + + /** + * Prepares and returns the RecipeDeleteCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipeDeleteCommand object. + * @throws CommandException If the number of arguments exceed valid amount. + */ + private RecipeDeleteCommand prepareRecipeDeleteCommand(String[] subCommandTokens) throws CommandException { + checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_WITH_ARGUMENT_LENGTH); + String recipeName = subCommandTokens[SUBARGUMENT_STARTING_INDEX]; + return new RecipeDeleteCommand(recipeName); + } +} diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 81b6496e4b..76258474d0 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -28,4 +28,5 @@ public class ErrorMessages { public static final String FILE_WRITE_ERROR_MESSAGE = "Something went wrong while writing result to output file"; public static final String FILE_READ_ERROR_MESSAGE = "Something went wrong when reading from the file"; public static final String DIRECTORY_INSTANTIATION_FAILED_MESSAGE = "Failed to create the directory for "; + public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; } diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java index bc9707f9be..fa2961291e 100644 --- a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.commands.recipe.RecipeDeleteCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/commands/RecipeNewCommandTest.java index 1702a7fb16..b0954fe38f 100644 --- a/src/test/java/decodex/commands/RecipeNewCommandTest.java +++ b/src/test/java/decodex/commands/RecipeNewCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.commands.recipe.RecipeNewCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/commands/RecipePopCommandTest.java index bf7e17abee..cda96aac4f 100644 --- a/src/test/java/decodex/commands/RecipePopCommandTest.java +++ b/src/test/java/decodex/commands/RecipePopCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.commands.recipe.RecipePopCommand; import decodex.data.exception.RecipeException; import org.junit.jupiter.api.Test; diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java index 9ab593899d..211e39f27e 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; +import decodex.commands.recipe.RecipePushCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/commands/RecipeResetCommandTest.java index a99aa15df0..da234f4f94 100644 --- a/src/test/java/decodex/commands/RecipeResetCommandTest.java +++ b/src/test/java/decodex/commands/RecipeResetCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import decodex.commands.recipe.RecipeResetCommand; import decodex.data.DataManager; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; diff --git a/src/test/java/decodex/commands/RecipeSelectCommandTest.java b/src/test/java/decodex/commands/RecipeSelectCommandTest.java index 3bbac451d0..e917b37735 100644 --- a/src/test/java/decodex/commands/RecipeSelectCommandTest.java +++ b/src/test/java/decodex/commands/RecipeSelectCommandTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; +import decodex.commands.recipe.RecipeSelectCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.RecipeManagerException; diff --git a/src/test/java/decodex/parser/RecipeCommandParserTest.java b/src/test/java/decodex/parser/RecipeCommandParserTest.java new file mode 100644 index 0000000000..925e0cf04f --- /dev/null +++ b/src/test/java/decodex/parser/RecipeCommandParserTest.java @@ -0,0 +1,130 @@ +package decodex.parser; + +import decodex.commands.Command; +import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.commands.recipe.RecipeNewCommand; +import decodex.commands.recipe.RecipePopCommand; +import decodex.commands.recipe.RecipePushCommand; +import decodex.commands.recipe.RecipeResetCommand; +import decodex.commands.recipe.RecipeSelectCommand; +import decodex.data.exception.CommandException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class RecipeCommandParserTest { + + RecipeCommandParser recipeCommandParser; + + @BeforeEach + public void setupRecipeCommandParser() { + recipeCommandParser = new RecipeCommandParser(); + } + + @Test + public void parseCommand_validRecipeNew_expectRecipeNewCommand() throws CommandException { + String[] recipeCommandArguments = {"new", "testRecipe"}; + Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + assertTrue(recipeCommand instanceof RecipeNewCommand); + } + + @Test + public void parseCommand_recipeNewWithoutRecipeName_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"new"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + @Test + public void parseCommand_recipeNewAdditionalArguments_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"new", "testRecipe", "1"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + @Test + public void parseCommand_validRecipeSelect_expectRecipeSelectCommand() throws CommandException { + String[] recipeCommandArguments = {"select", "testRecipe"}; + Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + assertTrue(recipeCommand instanceof RecipeSelectCommand); + } + + @Test + public void parseCommand_recipeSelectWithoutRecipeName_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"select"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + + @Test + public void parseCommand_recipeSelectAdditionalArguments_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"select", "testRecipe", "1"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + + @Test + public void parseCommand_validRecipePush_expectRecipePushCommand() throws CommandException { + String[] recipeCommandArguments = {"push", "base64encode"}; + Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + assertTrue(recipeCommand instanceof RecipePushCommand); + } + + @Test + public void parseCommand_validRecipePushWithParameters_expectRecipePushCommand() throws CommandException { + String[] recipeCommandArguments = {"push", "rotencode", "13"}; + Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + assertTrue(recipeCommand instanceof RecipePushCommand); + } + + @Test + public void parseCommand_recipePushWithoutModuleName_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"push"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + @Test + public void parseCommand_validRecipePop_expectRecipePopCommand() throws CommandException { + String[] recipeCommandArguments = {"pop"}; + Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + assertTrue(recipeCommand instanceof RecipePopCommand); + } + + @Test + public void parseCommand_recipePopAdditionalArguments_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"pop", "1"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + @Test + public void parseCommand_validRecipeReset_expectRecipeResetCommand() throws CommandException { + String[] recipeCommandArguments = {"reset"}; + Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + assertTrue(recipeCommand instanceof RecipeResetCommand); + } + + @Test + public void parseCommand_recipeResetAdditionalArguments_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"reset", "1"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + @Test + public void parseCommand_validRecipeDelete_expectRecipeDeleteCommand() throws CommandException { + String[] recipeCommandArguments = {"delete", "testRecipe"}; + Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + assertTrue(recipeCommand instanceof RecipeDeleteCommand); + } + + @Test + public void parseCommand_recipeDeleteNoRecipeName_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"delete"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } + + @Test + public void parseCommand_recipeDeleteAdditionalArguments_expectCommandExceptionThrown() { + String[] recipeCommandArguments = {"delete", "testRecipe", "1"}; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + } +} From b306fa5657e64d97771240065500f2166e1fdccd Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:15:39 +0800 Subject: [PATCH 320/607] Update text-ui-test --- text-ui-test/EXPECTED.TXT | 54 +++++++++++++++++++++++++++++---------- text-ui-test/input.txt | 10 ++++++++ 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index ac99c91ed7..35eb8184b7 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,15 +1,43 @@ ======================== Decodex =========================== Welcome to Decodex, transforming data with ease -============================================================ -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Unknown command, please enter a valid command -Decodex > [+] Input: "I am a lab rat" -Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "4920616d2061206c616220726174" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "L dp d ode udw" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Reverted all changes -Decodex > [x] Unknown command, please enter a valid command -Decodex > Goodbye! +============================================================ +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Your command cannot be blank +Decodex > [+] Input: "I am a lab rat" +Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "4920616d2061206c616220726174" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "L dp d ode udw" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Reverted all changes +Decodex > Here are the list of supported modules: + base64encode - Encodes the data using Base64 format. + base64decode - Decodes the data using Base64 format. + hexencode - Converts the input string to hexadecimal bytes + hexdecode - Converts a hexadecimal string back into its raw value + binencode - Encodes the data into binary. + bindecode - Decodes the data from binary. + rotencode - Rotates the alphabets in the input string by a specified amount + +You do not have any recipes + +Decodex > [x] The given recipe could not be found. +Decodex > [+] Recipe testRecipe created successfully + +Decodex > Here are the list of available recipes: + testRecipe + +Decodex > [+] There are no modules in the recipe +[+] Output: "I am a lab rat" +Decodex > [+] Added hexencode module to recipe testRecipe + +Decodex > [+] Added base64encode module to recipe testRecipe + +Decodex > [+] Added rotencode module to recipe testRecipe + +Decodex > [+] Modules executed: hexencode, base64encode, rotencode +[+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" +Decodex > [x] Unknown command, please enter a valid command +Decodex > Goodbye! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 724c829135..039b5f7b53 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,5 +1,6 @@ whoami bring me thanos + input I am a lab rat select module base64encode select module base64decode @@ -8,5 +9,14 @@ select module hexdecode select module rotencode 3 select module rotencode -3 reset +list +select recipe testRecipe +recipe new testRecipe +list recipes +select recipe testRecipe +recipe push hexencode +recipe push base64encode +recipe push rotencode 3 +select recipe testRecipe bye exit From dafc60a56c3b1d241c5173ef87f9468430a8071e Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:21:16 +0800 Subject: [PATCH 321/607] Update JavaDocs --- .../decodex/parser/RecipeCommandParser.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index 2468805c89..18be181f7a 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -31,6 +31,13 @@ public class RecipeCommandParser { public RecipeCommandParser() { } + /** + * Parses the subcommand tokens and returns its corresponding recipe command. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The respective recipe command. + * @throws CommandException If the number of arguments is invalid. + */ public Command parseCommand(String[] subCommandTokens) throws CommandException { assert subCommandTokens != null : "subCommandTokens should not be null"; @@ -73,7 +80,7 @@ public Command parseCommand(String[] subCommandTokens) throws CommandException { * * @param subCommandTokens The list of recipe subcommand tokens. * @param numberOfArguments The valid number of arguments. - * @throws CommandException If the number of arguments is not valid. + * @throws CommandException If the number of arguments is invalid. */ private void checkValidNumberOfArguments(String[] subCommandTokens, int numberOfArguments) throws CommandException { @@ -87,7 +94,7 @@ private void checkValidNumberOfArguments(String[] subCommandTokens, int numberOf * * @param subCommandTokens The list of recipe subcommand tokens. * @return The RecipeNewCommand object. - * @throws CommandException If the number of arguments exceed valid amount. + * @throws CommandException If the number of arguments is invalid. */ private RecipeNewCommand prepareRecipeNewCommand(String[] subCommandTokens) throws CommandException { checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_WITH_ARGUMENT_LENGTH); @@ -100,7 +107,7 @@ private RecipeNewCommand prepareRecipeNewCommand(String[] subCommandTokens) thro * * @param subCommandTokens The list of recipe subcommand tokens. * @return The RecipeSelectCommand object. - * @throws CommandException If the number of arguments exceed valid amount. + * @throws CommandException If the number of arguments is invalid. */ private RecipeSelectCommand prepareRecipeSelectCommand(String[] subCommandTokens) throws CommandException { checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_WITH_ARGUMENT_LENGTH); @@ -113,7 +120,7 @@ private RecipeSelectCommand prepareRecipeSelectCommand(String[] subCommandTokens * * @param subCommandTokens The list of recipe subcommand tokens. * @return The RecipePushCommand object. - * @throws CommandException If the number of arguments exceed valid amount. + * @throws CommandException If the number of arguments is invalid. */ private RecipePushCommand prepareRecipePushCommand(String[] subCommandTokens) throws CommandException { if (subCommandTokens.length < SUBCOMMAND_WITH_ARGUMENT_LENGTH) { @@ -131,7 +138,7 @@ private RecipePushCommand prepareRecipePushCommand(String[] subCommandTokens) th * * @param subCommandTokens The list of recipe subcommand tokens. * @return The RecipePopCommand object. - * @throws CommandException If the number of arguments exceed valid amount. + * @throws CommandException If the number of arguments is invalid. */ private RecipePopCommand prepareRecipePopCommand(String[] subCommandTokens) throws CommandException { checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_NO_ARGUMENT_LENGTH); @@ -143,7 +150,7 @@ private RecipePopCommand prepareRecipePopCommand(String[] subCommandTokens) thro * * @param subCommandTokens The list of recipe subcommand tokens. * @return The RecipeResetCommand object. - * @throws CommandException If the number of arguments exceed valid amount. + * @throws CommandException If the number of arguments is invalid. */ private RecipeResetCommand prepareRecipeResetCommand(String[] subCommandTokens) throws CommandException { checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_NO_ARGUMENT_LENGTH); @@ -155,7 +162,7 @@ private RecipeResetCommand prepareRecipeResetCommand(String[] subCommandTokens) * * @param subCommandTokens The list of recipe subcommand tokens. * @return The RecipeDeleteCommand object. - * @throws CommandException If the number of arguments exceed valid amount. + * @throws CommandException If the number of arguments is invalid. */ private RecipeDeleteCommand prepareRecipeDeleteCommand(String[] subCommandTokens) throws CommandException { checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_WITH_ARGUMENT_LENGTH); From f5e96c5fbd4e0bbb5893d91eee79abba631d3e13 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:21:37 +0800 Subject: [PATCH 322/607] Remove redundant constant --- src/main/java/decodex/parser/RecipeCommandParser.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index 18be181f7a..ac80b5a0e1 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -17,11 +17,6 @@ */ public class RecipeCommandParser { - /** - * Specifies the token used to split the user input by. - */ - private static final String SPLIT_REGEX = "\\s+"; - private static final int SUBCOMMAND_WORD_INDEX = 0; private static final int SUBARGUMENT_STARTING_INDEX = 1; private static final int MODULE_PARAMETERS_STARTING_INDEX = 2; From 7be5c29fd57fd5a7830875623bd5d17ba086786a Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:26:27 +0800 Subject: [PATCH 323/607] Convert `EXPECTED.txt` from CRLF to LF --- text-ui-test/EXPECTED.TXT | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 35eb8184b7..4660bade9c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,17 +1,17 @@ ======================== Decodex =========================== Welcome to Decodex, transforming data with ease -============================================================ -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Your command cannot be blank -Decodex > [+] Input: "I am a lab rat" -Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "4920616d2061206c616220726174" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "L dp d ode udw" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Reverted all changes +============================================================ +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Your command cannot be blank +Decodex > [+] Input: "I am a lab rat" +Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "4920616d2061206c616220726174" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "L dp d ode udw" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Reverted all changes Decodex > Here are the list of supported modules: base64encode - Encodes the data using Base64 format. base64decode - Decodes the data using Base64 format. @@ -20,24 +20,24 @@ Decodex > Here are the list of supported modules: binencode - Encodes the data into binary. bindecode - Decodes the data from binary. rotencode - Rotates the alphabets in the input string by a specified amount - + You do not have any recipes - -Decodex > [x] The given recipe could not be found. + +Decodex > [x] The given recipe could not be found. Decodex > [+] Recipe testRecipe created successfully - + Decodex > Here are the list of available recipes: testRecipe - -Decodex > [+] There are no modules in the recipe -[+] Output: "I am a lab rat" + +Decodex > [+] There are no modules in the recipe +[+] Output: "I am a lab rat" Decodex > [+] Added hexencode module to recipe testRecipe - + Decodex > [+] Added base64encode module to recipe testRecipe - + Decodex > [+] Added rotencode module to recipe testRecipe - -Decodex > [+] Modules executed: hexencode, base64encode, rotencode -[+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" -Decodex > [x] Unknown command, please enter a valid command -Decodex > Goodbye! + +Decodex > [+] Modules executed: hexencode, base64encode, rotencode +[+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" +Decodex > [x] Unknown command, please enter a valid command +Decodex > Goodbye! From f22a65be00e9a98e4e708344f80975ca0e1828c9 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:48:37 +0800 Subject: [PATCH 324/607] Update `Parser` to pass `userInput` to `RceipeCommandParser` --- src/main/java/decodex/parser/Parser.java | 8 +-- .../decodex/parser/RecipeCommandParser.java | 11 ++-- .../parser/RecipeCommandParserTest.java | 64 +++++++++---------- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 6cfb4ec913..4094fa8e5e 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -25,7 +25,7 @@ public class Parser { /** * Specifies the starting index where the arguments can be found. */ - private static final int STARTING_ARGUMENTS_INDEX = 1; + protected static final int STARTING_ARGUMENTS_INDEX = 1; /** * Specifies the token used to split the user input by. @@ -143,7 +143,7 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc * @param userInput The user input specified by the user. * @return A list of tokens. */ - private String[] getTokens(String userInput) { + protected String[] getTokens(String userInput) { String strippedUserInput = userInput.stripLeading(); return strippedUserInput.split(SPLIT_REGEX); } @@ -239,8 +239,6 @@ private SelectCommand prepareSelectCommand(String userInput) throws CommandExcep * @throws CommandException If the recipe command is invalid. */ private Command prepareRecipeSubcommands(String userInput) throws CommandException { - String[] tokens = getTokens(userInput); - String[] recipeCommandArguments = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); - return recipeCommandParser.parseCommand(recipeCommandArguments); + return recipeCommandParser.parseCommand(userInput); } } diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index ac80b5a0e1..51105909cc 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -15,7 +15,7 @@ /** * The RecipeCommandParser class handles the parsing and validation of recipe commands. */ -public class RecipeCommandParser { +public class RecipeCommandParser extends Parser { private static final int SUBCOMMAND_WORD_INDEX = 0; private static final int SUBARGUMENT_STARTING_INDEX = 1; @@ -29,12 +29,15 @@ public RecipeCommandParser() { /** * Parses the subcommand tokens and returns its corresponding recipe command. * - * @param subCommandTokens The list of recipe subcommand tokens. + * @param userInput The user input specified by the user. * @return The respective recipe command. * @throws CommandException If the number of arguments is invalid. */ - public Command parseCommand(String[] subCommandTokens) throws CommandException { - assert subCommandTokens != null : "subCommandTokens should not be null"; + public Command parseCommand(String userInput) throws CommandException { + assert userInput != null : "userInput should not be null"; + + String[] tokens = getTokens(userInput); + String[] subCommandTokens = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); // Check minimum subcommand length if (subCommandTokens.length < SUBCOMMAND_NO_ARGUMENT_LENGTH) { diff --git a/src/test/java/decodex/parser/RecipeCommandParserTest.java b/src/test/java/decodex/parser/RecipeCommandParserTest.java index 925e0cf04f..e5445b081c 100644 --- a/src/test/java/decodex/parser/RecipeCommandParserTest.java +++ b/src/test/java/decodex/parser/RecipeCommandParserTest.java @@ -25,106 +25,106 @@ public void setupRecipeCommandParser() { @Test public void parseCommand_validRecipeNew_expectRecipeNewCommand() throws CommandException { - String[] recipeCommandArguments = {"new", "testRecipe"}; - Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + String userInput = "recipe new testRecipe"; + Command recipeCommand = recipeCommandParser.parseCommand(userInput); assertTrue(recipeCommand instanceof RecipeNewCommand); } @Test public void parseCommand_recipeNewWithoutRecipeName_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"new"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe new"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_recipeNewAdditionalArguments_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"new", "testRecipe", "1"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe new testRecipe 1"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_validRecipeSelect_expectRecipeSelectCommand() throws CommandException { - String[] recipeCommandArguments = {"select", "testRecipe"}; - Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + String userInput = "recipe select testRecipe"; + Command recipeCommand = recipeCommandParser.parseCommand(userInput); assertTrue(recipeCommand instanceof RecipeSelectCommand); } @Test public void parseCommand_recipeSelectWithoutRecipeName_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"select"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe select"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_recipeSelectAdditionalArguments_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"select", "testRecipe", "1"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe select testRecipe 1"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_validRecipePush_expectRecipePushCommand() throws CommandException { - String[] recipeCommandArguments = {"push", "base64encode"}; - Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + String userInput = "recipe push base64encode"; + Command recipeCommand = recipeCommandParser.parseCommand(userInput); assertTrue(recipeCommand instanceof RecipePushCommand); } @Test public void parseCommand_validRecipePushWithParameters_expectRecipePushCommand() throws CommandException { - String[] recipeCommandArguments = {"push", "rotencode", "13"}; - Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + String userInput = "recipe push rotencode 13"; + Command recipeCommand = recipeCommandParser.parseCommand(userInput); assertTrue(recipeCommand instanceof RecipePushCommand); } @Test public void parseCommand_recipePushWithoutModuleName_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"push"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe push"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_validRecipePop_expectRecipePopCommand() throws CommandException { - String[] recipeCommandArguments = {"pop"}; - Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + String userInput = "recipe pop"; + Command recipeCommand = recipeCommandParser.parseCommand(userInput); assertTrue(recipeCommand instanceof RecipePopCommand); } @Test public void parseCommand_recipePopAdditionalArguments_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"pop", "1"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe pop 1"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_validRecipeReset_expectRecipeResetCommand() throws CommandException { - String[] recipeCommandArguments = {"reset"}; - Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + String userInput = "recipe reset"; + Command recipeCommand = recipeCommandParser.parseCommand(userInput); assertTrue(recipeCommand instanceof RecipeResetCommand); } @Test public void parseCommand_recipeResetAdditionalArguments_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"reset", "1"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe reset 1"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_validRecipeDelete_expectRecipeDeleteCommand() throws CommandException { - String[] recipeCommandArguments = {"delete", "testRecipe"}; - Command recipeCommand = recipeCommandParser.parseCommand(recipeCommandArguments); + String userInput = "recipe delete testRecipe"; + Command recipeCommand = recipeCommandParser.parseCommand(userInput); assertTrue(recipeCommand instanceof RecipeDeleteCommand); } @Test public void parseCommand_recipeDeleteNoRecipeName_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"delete"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe delete"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } @Test public void parseCommand_recipeDeleteAdditionalArguments_expectCommandExceptionThrown() { - String[] recipeCommandArguments = {"delete", "testRecipe", "1"}; - assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(recipeCommandArguments)); + String userInput = "recipe delete testRecipe 1"; + assertThrows(CommandException.class, () -> recipeCommandParser.parseCommand(userInput)); } } From 3c36e5e5883deadfadfb37bb6f0c2adc25268db8 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 25 Oct 2021 23:56:23 +0800 Subject: [PATCH 325/607] Removed the additional parameter on Decodex's end --- src/main/java/decodex/Decodex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 934b160dc9..e9114d8236 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -49,7 +49,7 @@ private void initDecodex() { moduleManager = new ModuleManager(); parser = new Parser(); ui = new Ui(); - storage = new Storage(moduleManager); + storage = new Storage(); try { Recipe[] savedRecipes = loadSavedRecipes(); } catch (IOException | ModuleException | ModuleManagerException err) { From be6ef7a9dd9598ed0d37e5940d37f2a900f86530 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 25 Oct 2021 23:56:52 +0800 Subject: [PATCH 326/607] Added overriden toString() method for writing modules to file. --- src/main/java/decodex/modules/Module.java | 10 ++++++++++ src/main/java/decodex/modules/rot/RotEncoder.java | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/main/java/decodex/modules/Module.java b/src/main/java/decodex/modules/Module.java index b18d8008de..b9d9576ce3 100644 --- a/src/main/java/decodex/modules/Module.java +++ b/src/main/java/decodex/modules/Module.java @@ -24,6 +24,16 @@ public String getDescription() { return description; } + /** + * Returns a String formatted Module. + * + * @return The string formatted Module. + */ + @Override + public String toString() { + return name; + } + /** * Performs data processing on the provided Data object. Abstract method to be implemented by other modules. * diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java index 699f3fb489..7c99f8680f 100644 --- a/src/main/java/decodex/modules/rot/RotEncoder.java +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -71,4 +71,14 @@ private char rotateUppercaseAlphabet(char c, int rotateOffset) { int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + rotateOffset, ALPHABETS_COUNT); return (char) (UPPERCASE_A + updatedAlphabetIndex); } + + /** + * Returns a String formatted RotEncoder. + * + * @return The string formatted RotEncoder. + */ + @Override + public String toString() { + return super.toString() + " " + rotateOffset; + } } From 7c694b479363f4f44ca14a1909a2fc5e247eab21 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 25 Oct 2021 23:57:40 +0800 Subject: [PATCH 327/607] Big update Organized some of the methods and remove some magic literals. Also added the delete file method for recipes. --- src/main/java/decodex/storage/Storage.java | 174 +++++++++++++++------ 1 file changed, 128 insertions(+), 46 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index b81c168615..4c4eef66f4 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -9,6 +9,7 @@ import java.io.File; import java.io.FileNotFoundException; +import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -35,12 +36,25 @@ public class Storage { private static final String OUTPUT_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; + private static final String LINE_BREAK_REGEX = "\\r?\\n"; + + + /** + * Specifies the index for the corresponding fields. + */ + private static final int RECIPE_NAME_INDEX = 0; + private static final int MODULE_NAME_INDEX = 0; + + /** + * Other miscellaneous constants for condition checking. + */ + private static final int EMPTY_LENGTH = 0; + /** * Initializes a new Storage. * - * @param moduleManager */ - public Storage(ModuleManager moduleManager) { + public Storage() { } /** @@ -57,13 +71,16 @@ public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager) instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); File[] recipeFiles = getAllRecipeFiles(); - if (recipeFiles.length == 0) { + if (recipeFiles.length == EMPTY_LENGTH) { return null; } ArrayList tempRecipeList = new ArrayList<>(); for (File recipeFile : recipeFiles) { - Recipe recipe = readRecipeFromFile(recipeFile.getName(), recipeFile, moduleManager); + String recipeFilename = recipeFile.getName(); + // The recipe name exists on the index 0 of the file's name. + String recipeName = recipeFilename.split("[.]")[RECIPE_NAME_INDEX]; + Recipe recipe = readRecipeFromFile(recipeName, recipeFile, moduleManager); tempRecipeList.add(recipe); } @@ -100,14 +117,14 @@ private File[] getAllRecipeFiles() { private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, ModuleManager moduleManager) throws ModuleException, ModuleManagerException { Recipe recipe = new Recipe(recipeFileName); - String[] recipeLines = recipeContent.split("\\r?\\n"); + String[] recipeLines = recipeContent.split(LINE_BREAK_REGEX); for (String recipeLine : recipeLines) { if (recipeLine.isEmpty()) { continue; } String[] tokens = recipeLine.split(" "); - String moduleName = tokens[0]; + String moduleName = tokens[MODULE_NAME_INDEX]; String[] moduleParameters = Arrays.copyOfRange(tokens, 1, tokens.length); Module module = moduleManager.selectModule(moduleName, moduleParameters); recipe.push(module); @@ -130,31 +147,12 @@ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleM throws IOException, ModuleException, ModuleManagerException { Path recipeFilePath = recipeFile.toPath(); - byte[] recipeContentBytes = readFromFile(recipeFilePath); - String recipeContent = new String(recipeContentBytes); + byte[] recipeContentBytes = readContentFromFile(recipeFilePath); + String recipeContent = convertByteArrayToString(recipeContentBytes); Recipe loadedRecipe = parseContentToRecipe(recipeFilename, recipeContent, moduleManager); return loadedRecipe; } - /** - * Reads and returns the input from the provided file. - * - * @param fileName The name of the input file specified by the user. - * @return The byte contents from the input file. - * @throws IOException If something went wrong when reading the file or - * the input file does not exist. - */ - public byte[] readInputFromFile(String fileName) throws IOException { - instantiateDirectoryIfNotExist(DEFAULT_INPUT_DIRECTORY); - - File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); - File inputFile = new File(inputDirectory, fileName); - Path inputFilePath = inputFile.toPath(); - - byte[] inputContent = readFromFile(inputFilePath); - return inputContent; - } - /** * Reads and returns the recipe from the provided file. * @@ -170,11 +168,30 @@ public String readRecipeFromFile(String fileName) throws IOException { File recipeFile = new File(recipeDirectory, fileName); Path recipeFilePath = recipeFile.toPath(); - byte[] recipeContentBytes = readFromFile(recipeFilePath); + byte[] recipeContentBytes = readContentFromFile(recipeFilePath); String recipeContent = recipeContentBytes.toString(); return recipeContent; } + /** + * Reads and returns the input from the provided file. + * + * @param fileName The name of the input file specified by the user. + * @return The byte contents from the input file. + * @throws IOException If something went wrong when reading the file or + * the input file does not exist. + */ + public byte[] readInputFromFile(String fileName) throws IOException { + instantiateDirectoryIfNotExist(DEFAULT_INPUT_DIRECTORY); + + File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); + File inputFile = new File(inputDirectory, fileName); + Path inputFilePath = inputFile.toPath(); + + byte[] inputContent = readContentFromFile(inputFilePath); + return inputContent; + } + /** * Reads and returns the contents from the file. * @@ -183,7 +200,7 @@ public String readRecipeFromFile(String fileName) throws IOException { * @throws IOException If something went wrong when reading the file. * @throws FileNotFoundException If the input file does not exist. */ - private byte[] readFromFile(Path readFilePath) throws IOException, FileNotFoundException { + private byte[] readContentFromFile(Path readFilePath) throws IOException, FileNotFoundException { try { return Files.readAllBytes(readFilePath); } catch (FileNotFoundException err) { @@ -194,13 +211,13 @@ private byte[] readFromFile(Path readFilePath) throws IOException, FileNotFoundE } /** - * Writes the provided output into a file. + * Saves the provided output into a file. * * @param outputBytes The encoded or decoded output. * @throws IOException If something went wrong when creating the file * or when writing to the file. */ - public void writeOutputToFile(byte[] outputBytes) throws IOException { + public void saveOutputToFile(byte[] outputBytes) throws IOException { instantiateDirectoryIfNotExist(DEFAULT_OUTPUT_DIRECTORY); LocalDateTime currentDateTime = LocalDateTime.now(); @@ -210,49 +227,104 @@ public void writeOutputToFile(byte[] outputBytes) throws IOException { File outputDirectory = new File(DEFAULT_OUTPUT_DIRECTORY); File outputFile = new File(outputDirectory, newOutputFileName); - writeToFile(outputFile, outputBytes); + saveBytesToFile(outputFile, outputBytes); } /** - * Writes the provided recipe into a file. + * Saves the content bytes into the provided file. * - * @param recipeName The name of the recipe. - * @param recipeBytes The recipe formatted in bytes. + * @param writeFile The file to be saved to. + * @param contentBytes The bytes of the content to be saved. * @throws IOException If something went wrong when creating the file * or when writing to the file. */ - public void writeRecipeToFile(String recipeName, byte[] recipeBytes) throws IOException { + private void saveBytesToFile(File writeFile, byte[] contentBytes) throws IOException { + try { + Path writeFilePath = writeFile.toPath(); + Files.write(writeFilePath, contentBytes); + } catch (IOException err) { + throw new IOException(ErrorMessages.FILE_WRITE_ERROR_MESSAGE); + } + } + + /** + * Saves the provided recipe into a file. + * + * @param recipe The recipe to be saved. + * @throws IOException If something went wrong when creating the file + * or when writing to the file. + */ + public void saveRecipeToFile(Recipe recipe) throws IOException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); - String newRecipeFileName = recipeName + RECIPE_FILE_PREFIX; + String newRecipeFileName = recipe.getName() + RECIPE_FILE_PREFIX; File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); - writeToFile(outputRecipeFile, recipeBytes); + saveRecipeModulesToFile(outputRecipeFile, recipe); } /** - * Writes the contents into the provided file. + * Saves the list of recipe's modules into the file. * - * @param writeFile The file to be written to. - * @param contentBytes The bytes of the content to be written. + * @param writeFile The file to be saved to. + * @param recipeToBeSaved The recipe to be saved. * @throws IOException If something went wrong when creating the file * or when writing to the file. */ - private void writeToFile(File writeFile, byte[] contentBytes) throws IOException { + private void saveRecipeModulesToFile(File writeFile, Recipe recipeToBeSaved) throws IOException { + ArrayList modules = recipeToBeSaved.getModuleList(); + try { - writeFile.createNewFile(); - Path writeFilePath = writeFile.toPath(); - Files.write(writeFilePath, contentBytes); + FileWriter writer = new FileWriter(writeFile); + String formattedModuleList = formatModuleListForSaving(modules); + writer.write(formattedModuleList); + writer.close(); } catch (IOException err) { throw new IOException(ErrorMessages.FILE_WRITE_ERROR_MESSAGE); } } + /** + * Formats the list of modules for saving to file. + * + * @param modules The list of modules to be saved. + * @return The formatted list of modules. + */ + private String formatModuleListForSaving(ArrayList modules) { + if (modules.size() == EMPTY_LENGTH) { + return ""; + } + + String formattedModuleList = ""; + for (Module module : modules) { + formattedModuleList = formattedModuleList + module.toString() + "\n"; + } + return formattedModuleList; + } + + /** + * Deletes the saved recipe file. + * + * @param recipeName The name of the deleted recipe. + */ + private void deleteRecipeFile(String recipeName) { + String newRecipeFileName = recipeName + RECIPE_FILE_PREFIX; + + File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); + File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); + + if (!outputRecipeFile.exists()) { + return; + } + + outputRecipeFile.delete(); + } + /** * Instantiates the given directory if it does not exist yet. */ - public void instantiateDirectoryIfNotExist(String directoryName) throws IOException { + private void instantiateDirectoryIfNotExist(String directoryName) throws IOException { File outputDirectory = new File(directoryName); if (outputDirectory.exists()) { return; @@ -264,4 +336,14 @@ public void instantiateDirectoryIfNotExist(String directoryName) throws IOExcept throw new IOException(ErrorMessages.DIRECTORY_INSTANTIATION_FAILED_MESSAGE + directoryName); } } + + /** + * Converts a byte array to string value. + * + * @param contentBytes The bytes of the content. + * @return The string value of the content. + */ + private String convertByteArrayToString(byte[] contentBytes) { + return new String(contentBytes); + } } From d3c399d3cc614f5b1b3c2cc4b722a060e163ae59 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 07:37:18 +0800 Subject: [PATCH 328/607] Added some of the suggested changes #1. --- src/main/java/decodex/Decodex.java | 2 +- src/main/java/decodex/storage/Storage.java | 25 +++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index e9114d8236..cd5e939e1e 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -85,7 +85,7 @@ public void run() { } while (!(command instanceof ExitCommand)); } - public Recipe[] loadSavedRecipes() throws IOException, ModuleException, ModuleManagerException { + private Recipe[] loadSavedRecipes() throws IOException, ModuleException, ModuleManagerException { return storage.loadRecipesFromDirectory(moduleManager); } } diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 4c4eef66f4..523088e068 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -37,6 +37,8 @@ public class Storage { private static final String OUTPUT_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; private static final String LINE_BREAK_REGEX = "\\r?\\n"; + private static final String FILENAME_EXTENSION_SPLIT_REGEX = "[.]"; + /** @@ -62,7 +64,7 @@ public Storage() { * * @param moduleManager The ModuleManager object. * @return The list of Recipe objects. - * @throws IOException If something went wrong when reading the file. + * @throws IOException If an error occurred when reading the file. * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. */ @@ -78,8 +80,7 @@ public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager) ArrayList tempRecipeList = new ArrayList<>(); for (File recipeFile : recipeFiles) { String recipeFilename = recipeFile.getName(); - // The recipe name exists on the index 0 of the file's name. - String recipeName = recipeFilename.split("[.]")[RECIPE_NAME_INDEX]; + String recipeName = recipeFilename.split(FILENAME_EXTENSION_SPLIT_REGEX)[RECIPE_NAME_INDEX]; Recipe recipe = readRecipeFromFile(recipeName, recipeFile, moduleManager); tempRecipeList.add(recipe); } @@ -109,7 +110,7 @@ private File[] getAllRecipeFiles() { * * @param recipeFileName The name of the recipe file. * @param recipeContent The contents of the recipe file. - * @param moduleManager The ModuleManger object + * @param moduleManager The ModuleManger object. * @return The Recipe object. * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. @@ -139,7 +140,7 @@ private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, * @param recipeFile The recipe File object. * @param moduleManager The ModuleManager object. * @return The Recipe object. - * @throws IOException If something went wrong when reading the file. + * @throws IOException If an error occurred when reading the file. * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. */ @@ -158,7 +159,7 @@ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleM * * @param fileName The name of the recipe file specified by the user. * @return The string formatted recipe from the provided file. - * @throws IOException If something went wrong when reading the file or + * @throws IOException If an error occurred when reading the file or * the input file does not exist. */ public String readRecipeFromFile(String fileName) throws IOException { @@ -178,7 +179,7 @@ public String readRecipeFromFile(String fileName) throws IOException { * * @param fileName The name of the input file specified by the user. * @return The byte contents from the input file. - * @throws IOException If something went wrong when reading the file or + * @throws IOException If an error occurred when reading the file or * the input file does not exist. */ public byte[] readInputFromFile(String fileName) throws IOException { @@ -197,7 +198,7 @@ public byte[] readInputFromFile(String fileName) throws IOException { * * @param readFilePath The Path object of the file to be read. * @return The bytes of the read content. - * @throws IOException If something went wrong when reading the file. + * @throws IOException If an error occurred when reading the file. * @throws FileNotFoundException If the input file does not exist. */ private byte[] readContentFromFile(Path readFilePath) throws IOException, FileNotFoundException { @@ -214,7 +215,7 @@ private byte[] readContentFromFile(Path readFilePath) throws IOException, FileNo * Saves the provided output into a file. * * @param outputBytes The encoded or decoded output. - * @throws IOException If something went wrong when creating the file + * @throws IOException If an error occurred when creating the file * or when writing to the file. */ public void saveOutputToFile(byte[] outputBytes) throws IOException { @@ -235,7 +236,7 @@ public void saveOutputToFile(byte[] outputBytes) throws IOException { * * @param writeFile The file to be saved to. * @param contentBytes The bytes of the content to be saved. - * @throws IOException If something went wrong when creating the file + * @throws IOException If an error occurred when creating the file * or when writing to the file. */ private void saveBytesToFile(File writeFile, byte[] contentBytes) throws IOException { @@ -251,7 +252,7 @@ private void saveBytesToFile(File writeFile, byte[] contentBytes) throws IOExcep * Saves the provided recipe into a file. * * @param recipe The recipe to be saved. - * @throws IOException If something went wrong when creating the file + * @throws IOException If an error occurred when creating the file * or when writing to the file. */ public void saveRecipeToFile(Recipe recipe) throws IOException { @@ -269,7 +270,7 @@ public void saveRecipeToFile(Recipe recipe) throws IOException { * * @param writeFile The file to be saved to. * @param recipeToBeSaved The recipe to be saved. - * @throws IOException If something went wrong when creating the file + * @throws IOException If an error occurred when creating the file * or when writing to the file. */ private void saveRecipeModulesToFile(File writeFile, Recipe recipeToBeSaved) throws IOException { From 7173ce8a7045c7998b306cad4f5a608bdd32e464 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 08:13:46 +0800 Subject: [PATCH 329/607] Added some of the suggested changes. --- src/main/java/decodex/Decodex.java | 2 +- src/main/java/decodex/storage/Storage.java | 27 ++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index cd5e939e1e..bb78fed47a 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -86,6 +86,6 @@ public void run() { } private Recipe[] loadSavedRecipes() throws IOException, ModuleException, ModuleManagerException { - return storage.loadRecipesFromDirectory(moduleManager); + return storage.loadRecipesFromDirectory(moduleManager, ui); } } diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 523088e068..7c336771ce 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -5,6 +5,7 @@ import decodex.modules.ModuleManager; import decodex.modules.Module; import decodex.recipes.Recipe; +import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; import java.io.File; @@ -46,6 +47,7 @@ public class Storage { */ private static final int RECIPE_NAME_INDEX = 0; private static final int MODULE_NAME_INDEX = 0; + private static final int STARTING_PARAMETER_INDEX = 1; /** * Other miscellaneous constants for condition checking. @@ -63,26 +65,32 @@ public Storage() { * Loads all the recipes previously saved in the recipe directory. * * @param moduleManager The ModuleManager object. + * @param ui The Ui object. * @return The list of Recipe objects. * @throws IOException If an error occurred when reading the file. * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. */ - public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager) + public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager, Ui ui) throws IOException, ModuleException, ModuleManagerException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); File[] recipeFiles = getAllRecipeFiles(); if (recipeFiles.length == EMPTY_LENGTH) { - return null; + return new Recipe[EMPTY_LENGTH]; } - + boolean hasError = false; ArrayList tempRecipeList = new ArrayList<>(); for (File recipeFile : recipeFiles) { String recipeFilename = recipeFile.getName(); String recipeName = recipeFilename.split(FILENAME_EXTENSION_SPLIT_REGEX)[RECIPE_NAME_INDEX]; - Recipe recipe = readRecipeFromFile(recipeName, recipeFile, moduleManager); - tempRecipeList.add(recipe); + try { + Recipe recipe = readRecipeFromFile(recipeName, recipeFile, moduleManager); + tempRecipeList.add(recipe); + } catch (IOException | ModuleException | ModuleManagerException err) { + ui.printError(err); + continue; + } } int tempRecipeListSize = tempRecipeList.size(); @@ -121,12 +129,13 @@ private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, String[] recipeLines = recipeContent.split(LINE_BREAK_REGEX); for (String recipeLine : recipeLines) { - if (recipeLine.isEmpty()) { + String trimmedRecipeLine = recipeLine.trim(); + if (trimmedRecipeLine.isEmpty()) { continue; } - String[] tokens = recipeLine.split(" "); + String[] tokens = trimmedRecipeLine.split(" "); String moduleName = tokens[MODULE_NAME_INDEX]; - String[] moduleParameters = Arrays.copyOfRange(tokens, 1, tokens.length); + String[] moduleParameters = Arrays.copyOfRange(tokens, STARTING_PARAMETER_INDEX, tokens.length); Module module = moduleManager.selectModule(moduleName, moduleParameters); recipe.push(module); } @@ -293,7 +302,7 @@ private void saveRecipeModulesToFile(File writeFile, Recipe recipeToBeSaved) thr * @return The formatted list of modules. */ private String formatModuleListForSaving(ArrayList modules) { - if (modules.size() == EMPTY_LENGTH) { + if (modules.isEmpty()) { return ""; } From ab8546b912172d25cdb095b5b046f31bbac07de0 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 08:20:41 +0800 Subject: [PATCH 330/607] Will revert this commit and remove the ui printing from storage side, if this is not ideal. --- src/main/java/decodex/Decodex.java | 4 ++-- src/main/java/decodex/storage/Storage.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index bb78fed47a..efbc5324a3 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -52,7 +52,7 @@ private void initDecodex() { storage = new Storage(); try { Recipe[] savedRecipes = loadSavedRecipes(); - } catch (IOException | ModuleException | ModuleManagerException err) { + } catch (IOException err) { ui.printError(err); } recipeManager = new RecipeManager(); @@ -85,7 +85,7 @@ public void run() { } while (!(command instanceof ExitCommand)); } - private Recipe[] loadSavedRecipes() throws IOException, ModuleException, ModuleManagerException { + private Recipe[] loadSavedRecipes() throws IOException{ return storage.loadRecipesFromDirectory(moduleManager, ui); } } diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 7c336771ce..0a5502be29 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -71,8 +71,7 @@ public Storage() { * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. */ - public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager, Ui ui) - throws IOException, ModuleException, ModuleManagerException { + public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager, Ui ui) throws IOException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); File[] recipeFiles = getAllRecipeFiles(); From 30efd7fb900bf2baad9f2453c83c18785f664188 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 08:29:08 +0800 Subject: [PATCH 331/607] Added a new error message for deletion of file, and also removed the redundant continue, since it is being handled to not throw back up. --- src/main/java/decodex/storage/Storage.java | 11 ++++++----- src/main/java/decodex/ui/messages/ErrorMessages.java | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 0a5502be29..4d0ba203f1 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -88,7 +88,6 @@ public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager, Ui ui) thr tempRecipeList.add(recipe); } catch (IOException | ModuleException | ModuleManagerException err) { ui.printError(err); - continue; } } @@ -178,7 +177,7 @@ public String readRecipeFromFile(String fileName) throws IOException { Path recipeFilePath = recipeFile.toPath(); byte[] recipeContentBytes = readContentFromFile(recipeFilePath); - String recipeContent = recipeContentBytes.toString(); + String recipeContent = convertByteArrayToString(recipeContentBytes); return recipeContent; } @@ -307,7 +306,7 @@ private String formatModuleListForSaving(ArrayList modules) { String formattedModuleList = ""; for (Module module : modules) { - formattedModuleList = formattedModuleList + module.toString() + "\n"; + formattedModuleList = formattedModuleList.concat(module.toString() + "\n"); } return formattedModuleList; } @@ -317,7 +316,7 @@ private String formatModuleListForSaving(ArrayList modules) { * * @param recipeName The name of the deleted recipe. */ - private void deleteRecipeFile(String recipeName) { + public void deleteRecipeFile(String recipeName) throws IOException { String newRecipeFileName = recipeName + RECIPE_FILE_PREFIX; File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); @@ -327,7 +326,9 @@ private void deleteRecipeFile(String recipeName) { return; } - outputRecipeFile.delete(); + if (!outputRecipeFile.delete()){ + throw new IOException(ErrorMessages.RECIPE_FILE_DELETE_FAILED_MESSAGE); + } } /** diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 81b6496e4b..019c9116ae 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -28,4 +28,5 @@ public class ErrorMessages { public static final String FILE_WRITE_ERROR_MESSAGE = "Something went wrong while writing result to output file"; public static final String FILE_READ_ERROR_MESSAGE = "Something went wrong when reading from the file"; public static final String DIRECTORY_INSTANTIATION_FAILED_MESSAGE = "Failed to create the directory for "; + public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; } From 5cc17eddaaeb8a098b31308d658d117a8680ac20 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 08:33:52 +0800 Subject: [PATCH 332/607] Some spacing adjustments. --- src/main/java/decodex/Decodex.java | 2 +- src/main/java/decodex/storage/Storage.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index efbc5324a3..c3a89e9b43 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -85,7 +85,7 @@ public void run() { } while (!(command instanceof ExitCommand)); } - private Recipe[] loadSavedRecipes() throws IOException{ + private Recipe[] loadSavedRecipes() throws IOException { return storage.loadRecipesFromDirectory(moduleManager, ui); } } diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 4d0ba203f1..2cfd24a342 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -326,7 +326,7 @@ public void deleteRecipeFile(String recipeName) throws IOException { return; } - if (!outputRecipeFile.delete()){ + if (!outputRecipeFile.delete()) { throw new IOException(ErrorMessages.RECIPE_FILE_DELETE_FAILED_MESSAGE); } } From 9c3f4e5997cf48f96f1d30252d52d28633186dad Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 26 Oct 2021 11:04:00 +0800 Subject: [PATCH 333/607] Update Binary Module * Move error message into `ErrorMessages` * Move `"0"` into a constant in `BinaryEncoder` --- src/main/java/decodex/modules/binary/BinaryDecoder.java | 3 ++- src/main/java/decodex/modules/binary/BinaryEncoder.java | 3 ++- src/main/java/decodex/ui/messages/ErrorMessages.java | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java index bb0d742e63..ee61890c48 100644 --- a/src/main/java/decodex/modules/binary/BinaryDecoder.java +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -5,6 +5,7 @@ import java.util.regex.Pattern; import decodex.data.Data; import decodex.data.exception.ModuleException; +import decodex.ui.messages.ErrorMessages; /** * The BinaryDecoder class handles the binary decoding operations. @@ -34,7 +35,7 @@ public Data run(Data data) throws ModuleException { String binString = data.toString(); if (!isValidBinary(binString)) { - throw new ModuleException("Invalid binary string"); + throw new ModuleException(ErrorMessages.BINARY_DECODING_FAILED_MESSAGE); } String[] binaryTokens = splitBinaryString(binString); diff --git a/src/main/java/decodex/modules/binary/BinaryEncoder.java b/src/main/java/decodex/modules/binary/BinaryEncoder.java index 61b6287a6c..97c113be1e 100644 --- a/src/main/java/decodex/modules/binary/BinaryEncoder.java +++ b/src/main/java/decodex/modules/binary/BinaryEncoder.java @@ -12,6 +12,7 @@ public class BinaryEncoder extends Module { public static final String MODULE_DESCRIPTION = "Encodes the data into binary."; private static final int BYTE_LENGTH = 8; + private static final String BINARY_ZERO_PREFIX = "0"; public BinaryEncoder() { super(MODULE_NAME, MODULE_DESCRIPTION); @@ -44,7 +45,7 @@ public Data run(Data data) { */ private String leftPad(String binString) { if (binString.length() < BYTE_LENGTH) { - return "0".repeat(BYTE_LENGTH - binString.length()) + binString; + return BINARY_ZERO_PREFIX.repeat(BYTE_LENGTH - binString.length()) + binString; } return binString; diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 3c3e8294ac..b68b3946ed 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -11,6 +11,7 @@ public class ErrorMessages { public static final String NO_DATA_FOUND = "No data found"; public static final String BASE64_DECODING_FAILED_MESSAGE = "Your data does not seem to be in Base64 format"; public static final String HEX_DECODING_FAILED_MESSAGE = "Invalid hexadecimal string"; + public static final String BINARY_DECODING_FAILED_MESSAGE = "Invalid binary string"; public static final String EMPTY_RECIPE_MESSAGE = "The recipe is currently empty. There is no module to be " + "removed."; public static final String INVALID_RECIPE_NAME = "Recipe names can contain only alphabets, numbers and " From 2830483eea0604371e0ca74a2a6c90a9fa1b38ce Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 26 Oct 2021 11:10:13 +0800 Subject: [PATCH 334/607] Add missing `ModuleException` JavaDocs --- src/main/java/decodex/modules/base64/Base64Decoder.java | 1 + src/main/java/decodex/modules/hex/HexDecoder.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index f3e3e9aa87..dfdad60c55 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -26,6 +26,7 @@ public Base64Decoder() { * * @param data The Data object of the encoded data. * @return The Data object of the Base64-decoded data. + * @throws ModuleException If the Data is not in base64 format. */ @Override public Data run(Data data) throws ModuleException { diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 2a01ba9576..8ceeae2ad2 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -27,6 +27,7 @@ public HexDecoder() { * * @param data Data object to be decoded. * @return new Data object representing the decoded input. + * @throws ModuleException If the Data is not in hexadecimal format. */ @Override public Data run(Data data) throws ModuleException { From 57f600e758bcaf9e6f18b3d3122c466ffa5fb4aa Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 26 Oct 2021 00:27:18 +0800 Subject: [PATCH 335/607] Add RecipeListCommand --- .../java/decodex/commands/ListCommand.java | 2 +- .../decodex/commands/RecipeListCommand.java | 58 +++++++++++ src/main/java/decodex/ui/Ui.java | 14 +++ .../decodex/ui/messages/RegularMessages.java | 5 +- .../commands/RecipeListCommandTest.java | 95 +++++++++++++++++++ 5 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 src/main/java/decodex/commands/RecipeListCommand.java create mode 100644 src/test/java/decodex/commands/RecipeListCommandTest.java diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index e4ca0390d0..30755c7efd 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -71,7 +71,7 @@ private String getModuleList(ModuleManager moduleManager) { } /** - * Prepares the list of available modules. + * Prepares the list of available recipes. * * @param recipeManager The RecipeManager to retrieve available recipes. * @return A formatted String of recipes. diff --git a/src/main/java/decodex/commands/RecipeListCommand.java b/src/main/java/decodex/commands/RecipeListCommand.java new file mode 100644 index 0000000000..b8dd4637fb --- /dev/null +++ b/src/main/java/decodex/commands/RecipeListCommand.java @@ -0,0 +1,58 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.RegularMessages; +import java.util.ArrayList; + +public class RecipeListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + private static final int ARRAY_INDEX_OFFSET = -1; + + private final String recipeName; + + public RecipeListCommand(String recipeName) { + super(); + this.recipeName = recipeName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, ModuleException, RecipeManagerException { + Recipe recipe; + + if (recipeName.isBlank()) { + recipe = recipeManager.getEditingRecipe(); + } else { + recipe = recipeManager.getRecipe(recipeName); + } + + ui.printRecipeModulesList(prepareModuleList(recipe), recipe.getName()); + } + + private String prepareModuleList(Recipe recipe) { + ArrayList recipeModuleList = recipe.getModuleList(); + + if (recipeModuleList.isEmpty()) { + return RegularMessages.RECIPE_EMPTY; + } + + String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); + StringBuilder recipeListString = new StringBuilder(); + + for (int index = 1; index <= recipeModuleNames.length; index++) { + recipeListString.append(String.format("%d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); + } + + return recipeListString.toString(); + } + +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 1237d4f2c8..3169f83fef 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -112,6 +112,20 @@ public void printRecipeModules(Recipe recipe) { printSuccess(message); } + /** + * Prints the list of modules in a recipe. + * + * @param moduleList The list of modules in the recipe. + * @param recipeName The name of the recipe. + */ + public void printRecipeModulesList(String moduleList, String recipeName) { + assert moduleList != null : "Module list should not be null"; + assert !moduleList.isBlank() : "Module list should not be empty"; + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + System.out.println(String.format(RegularMessages.LIST_RECIPE_MODULES + "\n%s", recipeName, moduleList)); + } + /** * Prints the message when a recipe is reset. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index f78f3619f5..24d1fec7eb 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -6,8 +6,9 @@ public class RegularMessages { + "Welcome to Decodex, transforming data with ease\n" + "============================================================"; public static final String GOODBYE = "Goodbye!"; - public static final String LIST_MODULES = "Here are the list of supported modules:"; - public static final String LIST_RECIPES = "Here are the list of available recipes:"; + public static final String LIST_MODULES = "Here is the list of supported modules:"; + public static final String LIST_RECIPES = "Here is the list of available recipes:"; + public static final String LIST_RECIPE_MODULES = "Here is the list of modules in recipe %s:"; public static final String NO_RECIPES = "You do not have any recipes"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; public static final String RECIPE_DELETED = "Recipe %s has been deleted"; diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java new file mode 100644 index 0000000000..332713873d --- /dev/null +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -0,0 +1,95 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.modules.base64.Base64Encoder; +import decodex.modules.binary.BinaryEncoder; +import decodex.modules.hex.HexEncoder; +import decodex.modules.rot.RotEncoder; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RecipeListCommandTest { + + private static final String TEST_RECIPE_NAME = "testRecipe"; + + private ByteArrayOutputStream outputStream; + private final PrintStream originalOutputStream = System.out; + + private final DataManager dataManager = new DataManager(); + private final ModuleManager moduleManager = new ModuleManager(); + private final RecipeManager recipeManager = new RecipeManager(); + private final Ui ui = new Ui(); + + @BeforeEach + public void createTestRecipe() throws RecipeManagerException { + Recipe recipe = new Recipe(TEST_RECIPE_NAME); + + recipe.push(new HexEncoder()); + recipe.push(new Base64Encoder()); + recipe.push(new RotEncoder(5)); + recipe.push(new BinaryEncoder()); + + recipeManager.addRecipe(recipe); + } + + @BeforeEach + public void setOutputStream() { + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + } + + @AfterEach + public void restoreOutputStream() { + System.setOut(originalOutputStream); + } + + @Test + void run_listRecipeName_success() throws RecipeManagerException, CommandException, ModuleException { + RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } + + @Test + void run_listBlank_expectException() { + RecipeListCommand testCommand = new RecipeListCommand(""); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } + + @Test + void run_listBlankHasEditingRecipe_success() throws RecipeManagerException, CommandException, ModuleException { + recipeManager.selectRecipeForEditing(TEST_RECIPE_NAME); + RecipeListCommand testCommand = new RecipeListCommand(""); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } + + @Test + void run_listUnknownRecipe_expectException() { + RecipeListCommand testCommand = new RecipeListCommand("unknownRecipe"); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } + + @Test + void run_listEmptyRecipe_expectException() throws RecipeManagerException, CommandException, ModuleException { + recipeManager.getRecipe(TEST_RECIPE_NAME).reset(); + RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } +} \ No newline at end of file From 8d9f0bfa04675c4674469b793a12949736dc2263 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 26 Oct 2021 01:33:34 +0800 Subject: [PATCH 336/607] Added requested changes --- .../java/decodex/commands/RecipeListCommand.java | 12 +++++++++--- .../java/decodex/commands/RecipeListCommandTest.java | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeListCommand.java b/src/main/java/decodex/commands/RecipeListCommand.java index b8dd4637fb..d1e752f9e3 100644 --- a/src/main/java/decodex/commands/RecipeListCommand.java +++ b/src/main/java/decodex/commands/RecipeListCommand.java @@ -35,10 +35,16 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec recipe = recipeManager.getRecipe(recipeName); } - ui.printRecipeModulesList(prepareModuleList(recipe), recipe.getName()); + ui.printRecipeModulesList(getModuleList(recipe), recipe.getName()); } - private String prepareModuleList(Recipe recipe) { + /** + * Gets and prepares an indexed list of the modules in the given recipe. + * + * @param recipe The recipe to list. + * @return A formatted, indexed list of modules in the recipe. + */ + private String getModuleList(Recipe recipe) { ArrayList recipeModuleList = recipe.getModuleList(); if (recipeModuleList.isEmpty()) { @@ -49,7 +55,7 @@ private String prepareModuleList(Recipe recipe) { StringBuilder recipeListString = new StringBuilder(); for (int index = 1; index <= recipeModuleNames.length; index++) { - recipeListString.append(String.format("%d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); + recipeListString.append(String.format(" %d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); } return recipeListString.toString(); diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index 332713873d..d3d19d8cb1 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -64,7 +64,7 @@ void run_listRecipeName_success() throws RecipeManagerException, CommandExceptio } @Test - void run_listBlank_expectException() { + void run_listBlankNoEditingRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand(""); assertThrows(RecipeManagerException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); @@ -92,4 +92,4 @@ void run_listEmptyRecipe_expectException() throws RecipeManagerException, Comman testCommand.run(dataManager, moduleManager, ui, recipeManager); assertFalse(outputStream.toString().isBlank()); } -} \ No newline at end of file +} From 802ca457e1b412a1a1bc16aaa53e3760b14e5b03 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Tue, 26 Oct 2021 14:43:08 +0800 Subject: [PATCH 337/607] Update UG introduction for V2.0 --- docs/UserGuide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index fe297e4dd1..4bb3c48e22 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -4,6 +4,8 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly transform data from one format to another with extreme ease**. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. +This guides serves to help you understand the usage of the program for quick and easy data manipulation. + > :information_source: This user guide is tailored for CTF players who have basic understanding of information security concepts. ![carbon(6).png](images/carbon(6).png) From 1ca5e1f08f2f97fa30eeeb97262e4ee57a98916f Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 14:50:05 +0800 Subject: [PATCH 338/607] Updated User Guide's TOC --- docs/UserGuide.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index fe297e4dd1..e1a428c92d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -9,15 +9,29 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CT ![carbon(6).png](images/carbon(6).png) ## Table of Contents -- [Introduction](#introduction) +- [Quick Start](#quick-start) - [Terminologies](#terminologies) +- [List of Available Modules](#list-of-available-modules) - [Features](#features) - - [Input of data: `input`](#input-of-data-input) - - [List available modules: `list`](#list-available-modules-list) - - [Selection of module: `select`](#selection-of-module-select) - - [Resetting of data: `reset`](#resetting-of-data-reset) - - [Exiting the program: `exit`](#exiting-the-program-exit) + - [Understanding the Application's Prompt](#understanding-the-application-prompt) + - [Basic Commands](#basic-commands) + - [Input of data: `input`](#input-of-data-input) + - [List available modules: `list`](#list-available-modules-list) + - [Selection of module: `select`](#selection-of-module-select) + - [Resetting of data: `reset`](#resetting-of-data-reset) + - [Exiting the program: `exit`](#exiting-the-program-exit) + - [Recipe Commands](#recipe-commands) + - [Create new recipe: `recipe`](#create-new-recipe) + - [Select recipe for editing: `recipe select`](#selecting-recipe-for-editing) + - [List modules in recipe: `recipe list`](#list-modules-in-recipe) + - [Add module to recipe: `recipe push`](#add-module-to-recipe) + - [Remove module from recipe: `recipe pop`](#remove-module-from-recipe) + - [Clear all modules in recipe: `recipe reset`](#clear-all-modules-in-recipe) + - [Delete recipe: `recipe delete`](#delete-recipe) + - [Saving Recipes to file `[coming in v2.0]`]() + - [Reading/writing data from/to file `[coming in v2.0]`]() - [Command Summary](#command-summary) +- [FAQ]() ## Terminologies From 7b630ed1e26667ff056d2cb6d6a56807bf9f6d5d Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 14:50:31 +0800 Subject: [PATCH 339/607] Add quick start --- docs/UserGuide.md | 20 ++++++++++++++++++++ docs/images/carbon(23).png | Bin 0 -> 18496 bytes 2 files changed, 20 insertions(+) create mode 100644 docs/images/carbon(23).png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index fe297e4dd1..33471d6bf9 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -30,6 +30,26 @@ This section serves to help the user better understand the terminologies used in | Base64, Hexadecimal | Common types of data encoding standards. | | Module | A self-contained set of instructions to process data into another form. | +## Quick Start +1. Ensure you have Java version 11 or above installed on your computer. + 1. If you haven't, you may download it [here](https://www.oracle.com/java/technologies/downloads/#java11-linux) + > ❗ You should download the installation relative to your Operating System. +2. Next, download the latest `decodex.jar` [here](https://github.com/AY2122S1-CS2113T-T10-3/tp/releases) + 1. Simply click on the `decodex.jar` under "Assets" and the download should start. +3. After downloading, you can open up `command prompt`. + > 💡To open command prompt, press `win + r` at the same time, then type and enter `cmd`. +4. Then, you can now run the `decodex.jar` by typing in `java -jar decodex.jar` and the Decodex's prompt should appear. Also, please ensure that you are at the directory where `decodex.jar` is. + 1. In the screenshot below, the `decodex.jar` is located in the `Downloads` folder. + ![carbon(23).png](images/carbon(23).png) +5. Here on, you can try out some of the basic commands below: + 1. `input I am groot`: Inputs into the program the data ("I am groot"). + 2. `list`: Lists all available modules and recipes that you can use. + 3. `select module base64encode`: Selects and runs the base64-encoding module on the data. + 4. `reset`: Resets the changes made to data - resetting to its original data. + 5. `exit`: Exits the application. + > 🎮 Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` is using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! +6. For more information on Decodex's features, please refer to the Features Section. + ## Features ### Input of data: `input` diff --git a/docs/images/carbon(23).png b/docs/images/carbon(23).png new file mode 100644 index 0000000000000000000000000000000000000000..cf921df8b7ba7cdf264c119a1b12c04bc9f2097b GIT binary patch literal 18496 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 Date: Tue, 26 Oct 2021 14:58:30 +0800 Subject: [PATCH 340/607] Update UG Terminology table --- docs/UserGuide.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index fe297e4dd1..785cfc410f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -23,12 +23,20 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CT This section serves to help the user better understand the terminologies used in this user guide. -| Terminology | Definition | -| ------------------- | ----------------------------------------------------------------------- | -| Encoding | Convert a message into a coded form. | -| Decoding | Convert a coded message into an intelligible form. | -| Base64, Hexadecimal | Common types of data encoding standards. | -| Module | A self-contained set of instructions to process data into another form. | +| Terminology | Definition | +|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Application, Program | Refers to the Decodex program. This two terms are used interchangeably in this User Guide. | +| Encoding | Convert a message into a coded form. | +| Decoding | Convert a coded message into an intelligible form. | +| Base64, Binary, Hexadecimal | Common types of data encoding standards. | +| Console | This refers to your command prompt window. | +| Argument | The additional information you provide to the program's command. | +| Module | A self-contained set of instructions to process your data into another form. | +| Recipe | Acts as a container for you to select your modules. When multiple modules are selected, this forms a "module chain". By default, you do not have any recipes. | +| 💡 | Represents a good tip for you. | +| ❗ | Represents something important that you should take note of. | +| ℹ | Represents additional information regarding commands/features for you to better understand how to use it. | +| 🎮 | Represents something optional that you can try out, mostly for exploring or fun. | ## Features From e09c625f0035c941727eab5a1bb98823336922a7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 14:58:49 +0800 Subject: [PATCH 341/607] Fix indenting --- docs/UserGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 33471d6bf9..d82460e194 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -37,7 +37,7 @@ This section serves to help the user better understand the terminologies used in 2. Next, download the latest `decodex.jar` [here](https://github.com/AY2122S1-CS2113T-T10-3/tp/releases) 1. Simply click on the `decodex.jar` under "Assets" and the download should start. 3. After downloading, you can open up `command prompt`. - > 💡To open command prompt, press `win + r` at the same time, then type and enter `cmd`. + > 💡To open command prompt, press `win + r` at the same time, then type and enter `cmd`. 4. Then, you can now run the `decodex.jar` by typing in `java -jar decodex.jar` and the Decodex's prompt should appear. Also, please ensure that you are at the directory where `decodex.jar` is. 1. In the screenshot below, the `decodex.jar` is located in the `Downloads` folder. ![carbon(23).png](images/carbon(23).png) @@ -47,7 +47,7 @@ This section serves to help the user better understand the terminologies used in 3. `select module base64encode`: Selects and runs the base64-encoding module on the data. 4. `reset`: Resets the changes made to data - resetting to its original data. 5. `exit`: Exits the application. - > 🎮 Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` is using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! + > 🎮 Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` is using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! 6. For more information on Decodex's features, please refer to the Features Section. ## Features From 7cb6257600f11ddb7698b515747beb9ade295d7b Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 26 Oct 2021 15:06:04 +0800 Subject: [PATCH 342/607] Fix exception --- src/test/java/decodex/commands/RecipeListCommandTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index d3d19d8cb1..f8a8c5699a 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -6,6 +6,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.modules.base64.Base64Encoder; @@ -34,7 +35,7 @@ class RecipeListCommandTest { private final Ui ui = new Ui(); @BeforeEach - public void createTestRecipe() throws RecipeManagerException { + public void createTestRecipe() throws RecipeManagerException, RecipeException { Recipe recipe = new Recipe(TEST_RECIPE_NAME); recipe.push(new HexEncoder()); From 29a79575ebb99122a493770d82fc50a37a416178 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 26 Oct 2021 00:27:18 +0800 Subject: [PATCH 343/607] Add RecipeListCommand --- .../java/decodex/commands/ListCommand.java | 2 +- .../decodex/commands/RecipeListCommand.java | 58 +++++++++++ src/main/java/decodex/ui/Ui.java | 14 +++ .../decodex/ui/messages/RegularMessages.java | 5 +- .../commands/RecipeListCommandTest.java | 95 +++++++++++++++++++ 5 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 src/main/java/decodex/commands/RecipeListCommand.java create mode 100644 src/test/java/decodex/commands/RecipeListCommandTest.java diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index e4ca0390d0..30755c7efd 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -71,7 +71,7 @@ private String getModuleList(ModuleManager moduleManager) { } /** - * Prepares the list of available modules. + * Prepares the list of available recipes. * * @param recipeManager The RecipeManager to retrieve available recipes. * @return A formatted String of recipes. diff --git a/src/main/java/decodex/commands/RecipeListCommand.java b/src/main/java/decodex/commands/RecipeListCommand.java new file mode 100644 index 0000000000..b8dd4637fb --- /dev/null +++ b/src/main/java/decodex/commands/RecipeListCommand.java @@ -0,0 +1,58 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.RegularMessages; +import java.util.ArrayList; + +public class RecipeListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + private static final int ARRAY_INDEX_OFFSET = -1; + + private final String recipeName; + + public RecipeListCommand(String recipeName) { + super(); + this.recipeName = recipeName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, ModuleException, RecipeManagerException { + Recipe recipe; + + if (recipeName.isBlank()) { + recipe = recipeManager.getEditingRecipe(); + } else { + recipe = recipeManager.getRecipe(recipeName); + } + + ui.printRecipeModulesList(prepareModuleList(recipe), recipe.getName()); + } + + private String prepareModuleList(Recipe recipe) { + ArrayList recipeModuleList = recipe.getModuleList(); + + if (recipeModuleList.isEmpty()) { + return RegularMessages.RECIPE_EMPTY; + } + + String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); + StringBuilder recipeListString = new StringBuilder(); + + for (int index = 1; index <= recipeModuleNames.length; index++) { + recipeListString.append(String.format("%d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); + } + + return recipeListString.toString(); + } + +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 1237d4f2c8..3169f83fef 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -112,6 +112,20 @@ public void printRecipeModules(Recipe recipe) { printSuccess(message); } + /** + * Prints the list of modules in a recipe. + * + * @param moduleList The list of modules in the recipe. + * @param recipeName The name of the recipe. + */ + public void printRecipeModulesList(String moduleList, String recipeName) { + assert moduleList != null : "Module list should not be null"; + assert !moduleList.isBlank() : "Module list should not be empty"; + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + System.out.println(String.format(RegularMessages.LIST_RECIPE_MODULES + "\n%s", recipeName, moduleList)); + } + /** * Prints the message when a recipe is reset. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index f78f3619f5..24d1fec7eb 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -6,8 +6,9 @@ public class RegularMessages { + "Welcome to Decodex, transforming data with ease\n" + "============================================================"; public static final String GOODBYE = "Goodbye!"; - public static final String LIST_MODULES = "Here are the list of supported modules:"; - public static final String LIST_RECIPES = "Here are the list of available recipes:"; + public static final String LIST_MODULES = "Here is the list of supported modules:"; + public static final String LIST_RECIPES = "Here is the list of available recipes:"; + public static final String LIST_RECIPE_MODULES = "Here is the list of modules in recipe %s:"; public static final String NO_RECIPES = "You do not have any recipes"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; public static final String RECIPE_DELETED = "Recipe %s has been deleted"; diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java new file mode 100644 index 0000000000..332713873d --- /dev/null +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -0,0 +1,95 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.modules.base64.Base64Encoder; +import decodex.modules.binary.BinaryEncoder; +import decodex.modules.hex.HexEncoder; +import decodex.modules.rot.RotEncoder; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RecipeListCommandTest { + + private static final String TEST_RECIPE_NAME = "testRecipe"; + + private ByteArrayOutputStream outputStream; + private final PrintStream originalOutputStream = System.out; + + private final DataManager dataManager = new DataManager(); + private final ModuleManager moduleManager = new ModuleManager(); + private final RecipeManager recipeManager = new RecipeManager(); + private final Ui ui = new Ui(); + + @BeforeEach + public void createTestRecipe() throws RecipeManagerException { + Recipe recipe = new Recipe(TEST_RECIPE_NAME); + + recipe.push(new HexEncoder()); + recipe.push(new Base64Encoder()); + recipe.push(new RotEncoder(5)); + recipe.push(new BinaryEncoder()); + + recipeManager.addRecipe(recipe); + } + + @BeforeEach + public void setOutputStream() { + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + } + + @AfterEach + public void restoreOutputStream() { + System.setOut(originalOutputStream); + } + + @Test + void run_listRecipeName_success() throws RecipeManagerException, CommandException, ModuleException { + RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } + + @Test + void run_listBlank_expectException() { + RecipeListCommand testCommand = new RecipeListCommand(""); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } + + @Test + void run_listBlankHasEditingRecipe_success() throws RecipeManagerException, CommandException, ModuleException { + recipeManager.selectRecipeForEditing(TEST_RECIPE_NAME); + RecipeListCommand testCommand = new RecipeListCommand(""); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } + + @Test + void run_listUnknownRecipe_expectException() { + RecipeListCommand testCommand = new RecipeListCommand("unknownRecipe"); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } + + @Test + void run_listEmptyRecipe_expectException() throws RecipeManagerException, CommandException, ModuleException { + recipeManager.getRecipe(TEST_RECIPE_NAME).reset(); + RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } +} \ No newline at end of file From ba7c36d2410a7ac74a7bea3ed28fcf367bba56da Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 26 Oct 2021 01:33:34 +0800 Subject: [PATCH 344/607] Added requested changes --- .../java/decodex/commands/RecipeListCommand.java | 12 +++++++++--- .../java/decodex/commands/RecipeListCommandTest.java | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeListCommand.java b/src/main/java/decodex/commands/RecipeListCommand.java index b8dd4637fb..d1e752f9e3 100644 --- a/src/main/java/decodex/commands/RecipeListCommand.java +++ b/src/main/java/decodex/commands/RecipeListCommand.java @@ -35,10 +35,16 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec recipe = recipeManager.getRecipe(recipeName); } - ui.printRecipeModulesList(prepareModuleList(recipe), recipe.getName()); + ui.printRecipeModulesList(getModuleList(recipe), recipe.getName()); } - private String prepareModuleList(Recipe recipe) { + /** + * Gets and prepares an indexed list of the modules in the given recipe. + * + * @param recipe The recipe to list. + * @return A formatted, indexed list of modules in the recipe. + */ + private String getModuleList(Recipe recipe) { ArrayList recipeModuleList = recipe.getModuleList(); if (recipeModuleList.isEmpty()) { @@ -49,7 +55,7 @@ private String prepareModuleList(Recipe recipe) { StringBuilder recipeListString = new StringBuilder(); for (int index = 1; index <= recipeModuleNames.length; index++) { - recipeListString.append(String.format("%d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); + recipeListString.append(String.format(" %d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); } return recipeListString.toString(); diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index 332713873d..d3d19d8cb1 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -64,7 +64,7 @@ void run_listRecipeName_success() throws RecipeManagerException, CommandExceptio } @Test - void run_listBlank_expectException() { + void run_listBlankNoEditingRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand(""); assertThrows(RecipeManagerException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); @@ -92,4 +92,4 @@ void run_listEmptyRecipe_expectException() throws RecipeManagerException, Comman testCommand.run(dataManager, moduleManager, ui, recipeManager); assertFalse(outputStream.toString().isBlank()); } -} \ No newline at end of file +} From baf779a86dbd62badb938661d6215ce9713e1960 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 26 Oct 2021 15:06:04 +0800 Subject: [PATCH 345/607] Fix exception --- src/test/java/decodex/commands/RecipeListCommandTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index d3d19d8cb1..f8a8c5699a 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -6,6 +6,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.modules.base64.Base64Encoder; @@ -34,7 +35,7 @@ class RecipeListCommandTest { private final Ui ui = new Ui(); @BeforeEach - public void createTestRecipe() throws RecipeManagerException { + public void createTestRecipe() throws RecipeManagerException, RecipeException { Recipe recipe = new Recipe(TEST_RECIPE_NAME); recipe.push(new HexEncoder()); From 6b567af668bb012737915960f6a03152f71e089c Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 26 Oct 2021 00:27:18 +0800 Subject: [PATCH 346/607] Add RecipeListCommand --- .../java/decodex/commands/ListCommand.java | 2 +- .../decodex/commands/RecipeListCommand.java | 58 +++++++++++ src/main/java/decodex/ui/Ui.java | 14 +++ .../decodex/ui/messages/RegularMessages.java | 5 +- .../commands/RecipeListCommandTest.java | 95 +++++++++++++++++++ 5 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 src/main/java/decodex/commands/RecipeListCommand.java create mode 100644 src/test/java/decodex/commands/RecipeListCommandTest.java diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index e4ca0390d0..30755c7efd 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -71,7 +71,7 @@ private String getModuleList(ModuleManager moduleManager) { } /** - * Prepares the list of available modules. + * Prepares the list of available recipes. * * @param recipeManager The RecipeManager to retrieve available recipes. * @return A formatted String of recipes. diff --git a/src/main/java/decodex/commands/RecipeListCommand.java b/src/main/java/decodex/commands/RecipeListCommand.java new file mode 100644 index 0000000000..b8dd4637fb --- /dev/null +++ b/src/main/java/decodex/commands/RecipeListCommand.java @@ -0,0 +1,58 @@ +package decodex.commands; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.RegularMessages; +import java.util.ArrayList; + +public class RecipeListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + private static final int ARRAY_INDEX_OFFSET = -1; + + private final String recipeName; + + public RecipeListCommand(String recipeName) { + super(); + this.recipeName = recipeName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws CommandException, ModuleException, RecipeManagerException { + Recipe recipe; + + if (recipeName.isBlank()) { + recipe = recipeManager.getEditingRecipe(); + } else { + recipe = recipeManager.getRecipe(recipeName); + } + + ui.printRecipeModulesList(prepareModuleList(recipe), recipe.getName()); + } + + private String prepareModuleList(Recipe recipe) { + ArrayList recipeModuleList = recipe.getModuleList(); + + if (recipeModuleList.isEmpty()) { + return RegularMessages.RECIPE_EMPTY; + } + + String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); + StringBuilder recipeListString = new StringBuilder(); + + for (int index = 1; index <= recipeModuleNames.length; index++) { + recipeListString.append(String.format("%d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); + } + + return recipeListString.toString(); + } + +} diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 1237d4f2c8..3169f83fef 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -112,6 +112,20 @@ public void printRecipeModules(Recipe recipe) { printSuccess(message); } + /** + * Prints the list of modules in a recipe. + * + * @param moduleList The list of modules in the recipe. + * @param recipeName The name of the recipe. + */ + public void printRecipeModulesList(String moduleList, String recipeName) { + assert moduleList != null : "Module list should not be null"; + assert !moduleList.isBlank() : "Module list should not be empty"; + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + System.out.println(String.format(RegularMessages.LIST_RECIPE_MODULES + "\n%s", recipeName, moduleList)); + } + /** * Prints the message when a recipe is reset. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index f78f3619f5..24d1fec7eb 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -6,8 +6,9 @@ public class RegularMessages { + "Welcome to Decodex, transforming data with ease\n" + "============================================================"; public static final String GOODBYE = "Goodbye!"; - public static final String LIST_MODULES = "Here are the list of supported modules:"; - public static final String LIST_RECIPES = "Here are the list of available recipes:"; + public static final String LIST_MODULES = "Here is the list of supported modules:"; + public static final String LIST_RECIPES = "Here is the list of available recipes:"; + public static final String LIST_RECIPE_MODULES = "Here is the list of modules in recipe %s:"; public static final String NO_RECIPES = "You do not have any recipes"; public static final String REVERTED_ALL_CHANGES = "Reverted all changes"; public static final String RECIPE_DELETED = "Recipe %s has been deleted"; diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java new file mode 100644 index 0000000000..332713873d --- /dev/null +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -0,0 +1,95 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.modules.base64.Base64Encoder; +import decodex.modules.binary.BinaryEncoder; +import decodex.modules.hex.HexEncoder; +import decodex.modules.rot.RotEncoder; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RecipeListCommandTest { + + private static final String TEST_RECIPE_NAME = "testRecipe"; + + private ByteArrayOutputStream outputStream; + private final PrintStream originalOutputStream = System.out; + + private final DataManager dataManager = new DataManager(); + private final ModuleManager moduleManager = new ModuleManager(); + private final RecipeManager recipeManager = new RecipeManager(); + private final Ui ui = new Ui(); + + @BeforeEach + public void createTestRecipe() throws RecipeManagerException { + Recipe recipe = new Recipe(TEST_RECIPE_NAME); + + recipe.push(new HexEncoder()); + recipe.push(new Base64Encoder()); + recipe.push(new RotEncoder(5)); + recipe.push(new BinaryEncoder()); + + recipeManager.addRecipe(recipe); + } + + @BeforeEach + public void setOutputStream() { + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + } + + @AfterEach + public void restoreOutputStream() { + System.setOut(originalOutputStream); + } + + @Test + void run_listRecipeName_success() throws RecipeManagerException, CommandException, ModuleException { + RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } + + @Test + void run_listBlank_expectException() { + RecipeListCommand testCommand = new RecipeListCommand(""); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } + + @Test + void run_listBlankHasEditingRecipe_success() throws RecipeManagerException, CommandException, ModuleException { + recipeManager.selectRecipeForEditing(TEST_RECIPE_NAME); + RecipeListCommand testCommand = new RecipeListCommand(""); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } + + @Test + void run_listUnknownRecipe_expectException() { + RecipeListCommand testCommand = new RecipeListCommand("unknownRecipe"); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + } + + @Test + void run_listEmptyRecipe_expectException() throws RecipeManagerException, CommandException, ModuleException { + recipeManager.getRecipe(TEST_RECIPE_NAME).reset(); + RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); + testCommand.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } +} \ No newline at end of file From 159550fe3ff98a5cd3b03d9d0d688329ba1b9e59 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 26 Oct 2021 01:33:34 +0800 Subject: [PATCH 347/607] Added requested changes --- .../java/decodex/commands/RecipeListCommand.java | 12 +++++++++--- .../java/decodex/commands/RecipeListCommandTest.java | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/commands/RecipeListCommand.java b/src/main/java/decodex/commands/RecipeListCommand.java index b8dd4637fb..d1e752f9e3 100644 --- a/src/main/java/decodex/commands/RecipeListCommand.java +++ b/src/main/java/decodex/commands/RecipeListCommand.java @@ -35,10 +35,16 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec recipe = recipeManager.getRecipe(recipeName); } - ui.printRecipeModulesList(prepareModuleList(recipe), recipe.getName()); + ui.printRecipeModulesList(getModuleList(recipe), recipe.getName()); } - private String prepareModuleList(Recipe recipe) { + /** + * Gets and prepares an indexed list of the modules in the given recipe. + * + * @param recipe The recipe to list. + * @return A formatted, indexed list of modules in the recipe. + */ + private String getModuleList(Recipe recipe) { ArrayList recipeModuleList = recipe.getModuleList(); if (recipeModuleList.isEmpty()) { @@ -49,7 +55,7 @@ private String prepareModuleList(Recipe recipe) { StringBuilder recipeListString = new StringBuilder(); for (int index = 1; index <= recipeModuleNames.length; index++) { - recipeListString.append(String.format("%d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); + recipeListString.append(String.format(" %d. %s\n", index, recipeModuleNames[index + ARRAY_INDEX_OFFSET])); } return recipeListString.toString(); diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index 332713873d..d3d19d8cb1 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -64,7 +64,7 @@ void run_listRecipeName_success() throws RecipeManagerException, CommandExceptio } @Test - void run_listBlank_expectException() { + void run_listBlankNoEditingRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand(""); assertThrows(RecipeManagerException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); @@ -92,4 +92,4 @@ void run_listEmptyRecipe_expectException() throws RecipeManagerException, Comman testCommand.run(dataManager, moduleManager, ui, recipeManager); assertFalse(outputStream.toString().isBlank()); } -} \ No newline at end of file +} From ddf7fdf97f6ec4802bbac28767f4185e688fd647 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 26 Oct 2021 15:06:04 +0800 Subject: [PATCH 348/607] Fix exception --- src/test/java/decodex/commands/RecipeListCommandTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index d3d19d8cb1..f8a8c5699a 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -6,6 +6,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.modules.base64.Base64Encoder; @@ -34,7 +35,7 @@ class RecipeListCommandTest { private final Ui ui = new Ui(); @BeforeEach - public void createTestRecipe() throws RecipeManagerException { + public void createTestRecipe() throws RecipeManagerException, RecipeException { Recipe recipe = new Recipe(TEST_RECIPE_NAME); recipe.push(new HexEncoder()); From f2fde53fb570e007955a038d3d16161326ef4f05 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 26 Oct 2021 15:18:53 +0800 Subject: [PATCH 349/607] fix expected --- text-ui-test/EXPECTED.TXT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 4660bade9c..0531722f7d 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -12,7 +12,7 @@ Decodex > [+] Output: "I am a lab rat" Decodex > [+] Output: "L dp d ode udw" Decodex > [+] Output: "I am a lab rat" Decodex > [+] Reverted all changes -Decodex > Here are the list of supported modules: +Decodex > Here is the list of supported modules: base64encode - Encodes the data using Base64 format. base64decode - Decodes the data using Base64 format. hexencode - Converts the input string to hexadecimal bytes @@ -26,7 +26,7 @@ You do not have any recipes Decodex > [x] The given recipe could not be found. Decodex > [+] Recipe testRecipe created successfully -Decodex > Here are the list of available recipes: +Decodex > Here is the list of available recipes: testRecipe Decodex > [+] There are no modules in the recipe From b06d494ab40fb79e305af7d187e1a65a0701c335 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 15:41:33 +0800 Subject: [PATCH 350/607] Add RecipeListCommand --- .../RecipeListCommandSequenceDiagram.puml | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml diff --git a/docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml new file mode 100644 index 0000000000..fdff2c1b7d --- /dev/null +++ b/docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml @@ -0,0 +1,41 @@ +@startuml +!include ../../Style.puml + +box LOGIC_COLOR +Participant ":RecipeListCommand" as RecipeListCommand +end box +box RECIPE_COLOR +Participant ":RecipeManager" as RecipeManager +end box +box UI_COLOR +Participant ":Ui" as Ui +end box + +-> RecipeListCommand: run() +activate RecipeListCommand + +alt recipeName is blank + RecipeListCommand -> RecipeManager: getEditingRecipe() + activate RecipeManager + RecipeListCommand <-- RecipeManager: :Recipe + deactivate RecipeManager +else recipeName is not blank + RecipeListCommand -> RecipeManager: getRecipe(:String) + activate RecipeManager + RecipeListCommand <-- RecipeManager: :Recipe + deactivate RecipeManager +end + +RecipeListCommand -> RecipeListCommand: getModuleList(:String) +activate RecipeListCommand +RecipeListCommand --> RecipeListCommand +deactivate RecipeListCommand + +RecipeListCommand -> Ui: printRecipeModulesList(:String, :String) +activate Ui +RecipeListCommand <-- Ui +deactivate Ui + +<-- RecipeListCommand + +@enduml \ No newline at end of file From ce9ddacc034f8acb54dabcfd71e91ea5580908db Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 19:04:23 +0800 Subject: [PATCH 351/607] Add RecipeCommandParser puml --- .../ModuleManagerClassDiagram.puml | 8 +-- .../ParserSequenceDiagram.puml | 21 +++++++- .../RecipeCommandParserSequenceDiagram.puml | 51 +++++++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 docs/puml/sequencediagrams/RecipeCommandParserSequenceDiagram.puml diff --git a/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml b/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml index c3f548530d..c452280528 100644 --- a/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml +++ b/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml @@ -16,15 +16,11 @@ class ModuleManager { + getModules(): String[][] + selectModule(moduleName: String, parameters: String[]): Module - hasNoParameters(parameters: String): void - - prepareXYZEncoder(parameters: String[]): Module - - prepareXYZDecoder(parameters: String[]): Module + - prepareXYZModule(parameters: String[]): Module } note right - XYZEncoder = HexEncoder, - Base64Encoder, etc - - XYZDecoder = HexDecoder, + XYZModule = HexEncoder, Base64Decoder, etc end note diff --git a/docs/puml/sequencediagrams/ParserSequenceDiagram.puml b/docs/puml/sequencediagrams/ParserSequenceDiagram.puml index 257a1cff6c..8d8adb5c39 100644 --- a/docs/puml/sequencediagrams/ParserSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/ParserSequenceDiagram.puml @@ -4,20 +4,26 @@ box LOGIC_COLOR Participant ":Parser" as Parser Participant ":XYZCommand" as XYZCommand +Participant ":RecipeCommandParser" as RecipeCommandParser end box activate Parser -> Parser : parseCommand(userInput : String) Parser -> Parser : getCommandType(userInput : String) activate Parser -Parser --> Parser : commandType +Parser --> Parser : :String deactivate Parser alt commandType is "xyz" + note left + "xyz" refers to basic commands + such as reset, list, exit. + end note Parser -> Parser : prepareXYZCommand() note right XYZCommand = InputCommand, SelectCommand, - ListCommand, etc + ListCommand, etc (Excluding + RecipeXYZCommand) end note activate Parser create XYZCommand @@ -32,6 +38,17 @@ alt commandType is "xyz" Parser --> Parser : XYZCommand deactivate Parser <-- Parser : XYZCommand +else commandType is "recipe" + Parser -> RecipeCommandParser: parseCommand(:String) + activate RecipeCommandParser + ref over RecipeCommandParser: Parsing of Recipe Command + Parser <-- RecipeCommandParser: RecipeXYZCommand + deactivate RecipeCommandParser + <-- Parser: RecipeXYZCommand + note left + RecipeXYZCommand = RecipePushCommand, + RecipePopCommand, etc + end note else commandType is not valid <-- Parser : ParserException end diff --git a/docs/puml/sequencediagrams/RecipeCommandParserSequenceDiagram.puml b/docs/puml/sequencediagrams/RecipeCommandParserSequenceDiagram.puml new file mode 100644 index 0000000000..70524ba6ef --- /dev/null +++ b/docs/puml/sequencediagrams/RecipeCommandParserSequenceDiagram.puml @@ -0,0 +1,51 @@ +@startuml +!include ../Style.puml + +box LOGIC_COLOR +Participant ":RecipeCommandParser" as RecipeCommandParser +Participant ":RecipeXYZCommand" as RecipeXYZCommand +end box + +-> RecipeCommandParser: parseCommand(:String) +activate RecipeCommandParser + +RecipeCommandParser -> RecipeCommandParser: getTokens(:String) +activate RecipeCommandParser + +RecipeCommandParser --> RecipeCommandParser +deactivate RecipeCommandParser + +alt length of subCommandTokens < SUBCOMMAND_NO_ARGUMENT_LENGTH + <-- RecipeCommandParser: CommandException +else length of subCommandTokens >= SUBCOMMAND_NO_ARGUMENT_LENGTH + alt subCommand = "xyz" + + RecipeCommandParser -> RecipeCommandParser: prepareRecipeXYZCommand + activate RecipeCommandParser + note left + "xyz" refers to recipe commands + such as push, pop, reset, etc + end note + note right + RecipeXYZCommand = RecipePushCommand, + RecipePopCommand, etc + end note + + create RecipeXYZCommand + RecipeCommandParser -> RecipeXYZCommand: new RecipeXYZCommand() + activate RecipeXYZCommand + + RecipeCommandParser <-- RecipeXYZCommand + deactivate RecipeXYZCommand + + RecipeCommandParser --> RecipeCommandParser: :RecipeXYZCommand + deactivate RecipeCommandParser + <-- RecipeCommandParser: :RecipeXYZCommand + else subCommand is not valid + <-- RecipeCommandParser: CommandException + end +end + +deactivate RecipeCommandParser + +@enduml From a274842dc688017cc19ec9019f766adf9b43a9e3 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 20:48:35 +0800 Subject: [PATCH 352/607] Add help command --- .../java/decodex/commands/ExitCommand.java | 2 + .../java/decodex/commands/HelpCommand.java | 45 +++++++++++++++++++ .../java/decodex/commands/InputCommand.java | 2 + .../java/decodex/commands/ListCommand.java | 4 ++ .../java/decodex/commands/ResetCommand.java | 2 + .../java/decodex/commands/SelectCommand.java | 2 + .../commands/recipe/RecipeDeleteCommand.java | 2 + .../commands/recipe/RecipeNewCommand.java | 2 + .../commands/recipe/RecipePopCommand.java | 2 + .../commands/recipe/RecipePushCommand.java | 2 + .../commands/recipe/RecipeResetCommand.java | 2 + .../commands/recipe/RecipeSelectCommand.java | 2 + src/main/java/decodex/parser/Parser.java | 13 ++++++ src/main/java/decodex/ui/Ui.java | 18 ++++++++ .../decodex/ui/messages/RegularMessages.java | 3 ++ .../decodex/commands/HelpCommandTest.java | 44 ++++++++++++++++++ 16 files changed, 147 insertions(+) create mode 100644 src/main/java/decodex/commands/HelpCommand.java create mode 100644 src/test/java/decodex/commands/HelpCommandTest.java diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java index 7258c015ed..581de092c4 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -8,6 +8,8 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; + public static final String COMMAND_HELP_MESSAGE = "Exit the program\n" + + "Syntax: exit"; public ExitCommand() { super(); diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java new file mode 100644 index 0000000000..4e23b3f9da --- /dev/null +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -0,0 +1,45 @@ +package decodex.commands; + +import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.commands.recipe.RecipeNewCommand; +import decodex.commands.recipe.RecipePopCommand; +import decodex.commands.recipe.RecipePushCommand; +import decodex.commands.recipe.RecipeResetCommand; +import decodex.commands.recipe.RecipeSelectCommand; +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +public class HelpCommand extends Command { + + public static final String COMMAND_WORD = "help"; + public static final String COMMAND_HELP_MESSAGE = "Shows list of available commands\n" + + "Syntax: help"; + + private static String[] BASIC_COMMAND_HELP_MESSAGES = { + HelpCommand.COMMAND_HELP_MESSAGE, + ExitCommand.COMMAND_HELP_MESSAGE, + InputCommand.COMMAND_HELP_MESSAGE, + ListCommand.COMMAND_HELP_MESSAGE, + SelectCommand.COMMAND_HELP_MESSAGE + }; + + private static String[] RECIPE_COMMAND_HELP_MESSAGES = { + RecipeNewCommand.COMMAND_HELP_MESSAGE, + RecipeSelectCommand.COMMAND_HELP_MESSAGE, + RecipePushCommand.COMMAND_HELP_MESSAGE, + RecipePopCommand.COMMAND_HELP_MESSAGE, + RecipeResetCommand.COMMAND_HELP_MESSAGE, + RecipeDeleteCommand.COMMAND_HELP_MESSAGE + }; + + public HelpCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { + ui.printCommandHelp(BASIC_COMMAND_HELP_MESSAGES, RECIPE_COMMAND_HELP_MESSAGES); + } +} diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index 05271357c0..752fe5d7e4 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -11,6 +11,8 @@ public class InputCommand extends Command { public static final String COMMAND_WORD = "input"; + public static final String COMMAND_HELP_MESSAGE = "Input of data\n" + + "Syntax: input "; private final String dataString; diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index e4ca0390d0..bcccf0e173 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -10,6 +10,10 @@ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; + public static final String COMMAND_HELP_MESSAGE = "List available modules and recipes\n" + + "Syntax: list {category}\n" + + "Acceptable values of category: modules, recipes"; + public static final String LIST_CATEGORY_MODULES = "modules"; public static final String LIST_CATEGORY_RECIPE = "recipes"; private static final int MODULE_NAME_INDEX = 0; diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java index 1dcdc7572b..b692ce7886 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -9,6 +9,8 @@ public class ResetCommand extends Command { public static final String COMMAND_WORD = "reset"; + public static final String COMMAND_HELP_MESSAGE = "Resets the data\n" + + "Syntax: reset\n"; public ResetCommand() { super(); diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index 1577513c99..b8abc22288 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -18,6 +18,8 @@ public class SelectCommand extends Command { public static final String COMMAND_WORD = "select"; + public static final String COMMAND_HELP_MESSAGE = "Selects a module or recipe to run\n" + + "Syntax: select module {moduleArgument}, select recipe "; private static final String SELECT_CATEGORY_MODULE = "module"; private static final String SELECT_CATEGORY_RECIPE = "recipe"; diff --git a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java index 8b0261eed0..be939265c7 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java @@ -13,6 +13,8 @@ public class RecipeDeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; + public static final String COMMAND_HELP_MESSAGE = "Delete recipe\n" + + "Syntax: recipe delete "; private final String recipeName; diff --git a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java index 78820561e0..dd5aa1f4d8 100644 --- a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java @@ -15,6 +15,8 @@ public class RecipeNewCommand extends Command { public static final String COMMAND_WORD = "new"; + public static final String COMMAND_HELP_MESSAGE = "Create new recipe\n" + + "Syntax: recipe new "; private final String recipeName; diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java index 627a318a26..b843758e3d 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -13,6 +13,8 @@ public class RecipePopCommand extends Command { public static final String COMMAND_WORD = "pop"; + public static final String COMMAND_HELP_MESSAGE = "Remove module from recipe\n" + + "Syntax: recipe pop"; public RecipePopCommand() { super(); diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java index 49eff97cc8..9c68498364 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -16,6 +16,8 @@ public class RecipePushCommand extends Command { public static final String COMMAND_WORD = "push"; + public static final String COMMAND_HELP_MESSAGE = "Add module to recipe\n" + + "Syntax: recipe push {moduleArgument}"; private final String moduleName; private final String[] parameters; diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java index 2d91f3d189..4c4ee0e639 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -11,6 +11,8 @@ public class RecipeResetCommand extends Command { public static final String COMMAND_WORD = "reset"; + public static final String COMMAND_HELP_MESSAGE = "Clear all modules from recipe\n" + + "Syntax: recipe reset"; public RecipeResetCommand() { super(); diff --git a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java index ac28cbdfd8..6a4f9b70be 100644 --- a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java @@ -12,6 +12,8 @@ public class RecipeSelectCommand extends Command { public static final String COMMAND_WORD = "select"; + public static final String COMMAND_HELP_MESSAGE = "Select recipe for editing\n" + + "Syntax: recipe select "; private final String recipeName; diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 4094fa8e5e..e89a50d1b5 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -8,6 +8,7 @@ import decodex.commands.ListCommand; import decodex.commands.ResetCommand; import decodex.commands.SelectCommand; +import decodex.commands.HelpCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; import decodex.ui.messages.ErrorMessages; @@ -128,6 +129,9 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc case SelectCommand.COMMAND_WORD: command = prepareSelectCommand(userInput); break; + case HelpCommand.COMMAND_WORD: + command = prepareHelpCommand(); + break; case RECIPE_COMMAND_WORD: command = prepareRecipeSubcommands(userInput); break; @@ -241,4 +245,13 @@ private SelectCommand prepareSelectCommand(String userInput) throws CommandExcep private Command prepareRecipeSubcommands(String userInput) throws CommandException { return recipeCommandParser.parseCommand(userInput); } + + /** + * Prepares and returns the HelpCommand. + * + * @return The HelpCommand object. + */ + private HelpCommand prepareHelpCommand() { + return new HelpCommand(); + } } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 1237d4f2c8..58005e1ac3 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -212,4 +212,22 @@ public void printNewRecipeCreated(String recipeName) { private void printPromptHeader() { System.out.print(PROMPT_HEADER); } + + /** + * Prints all supported commands and their respective help message. + * + * @param basicCommandHelpList List of basic command help messages. + * @param recipeCommandHelpList List of recipe command help messages. + */ + public void printCommandHelp(String[] basicCommandHelpList, String[] recipeCommandHelpList) { + System.out.println(RegularMessages.SYNTAX_MANDATORY_OPTIONAL + "\n"); + System.out.println(RegularMessages.BASIC_COMMANDS); + for (String commandFormat: basicCommandHelpList) { + System.out.printf("-> %s\n\n", commandFormat.replace("\n", "\n ")); + } + System.out.println(RegularMessages.RECIPE_COMMANDS); + for (String commandFormat: recipeCommandHelpList) { + System.out.printf("-> %s\n\n", commandFormat.replace("\n", "\n ")); + } + } } diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index f78f3619f5..916ea98d0f 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -18,4 +18,7 @@ public class RegularMessages { public static final String RECIPE_SELECT = "Recipe %s selected for editing"; public static final String RECIPE_EMPTY = "There are no modules in the recipe"; public static final String RECIPE_MODULES_EXECUTED = "Modules executed:"; + public static final String BASIC_COMMANDS = "Here are the basic commands:"; + public static final String RECIPE_COMMANDS = "Here are the recipe commands:"; + public static final String SYNTAX_MANDATORY_OPTIONAL = "Syntax: < > - mandatory, { } - optional"; } diff --git a/src/test/java/decodex/commands/HelpCommandTest.java b/src/test/java/decodex/commands/HelpCommandTest.java new file mode 100644 index 0000000000..5f7a12dcac --- /dev/null +++ b/src/test/java/decodex/commands/HelpCommandTest.java @@ -0,0 +1,44 @@ +package decodex.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +class HelpCommandTest { + + private ByteArrayOutputStream outputStream; + private final PrintStream originalOutputStream = System.out; + + private final DataManager dataManager = new DataManager(); + private final ModuleManager moduleManager = new ModuleManager(); + private final RecipeManager recipeManager = new RecipeManager(); + private final Ui ui = new Ui(); + + @BeforeEach + public void setOutputStream() { + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + } + + @AfterEach + public void restoreOutputStream() { + System.setOut(originalOutputStream); + } + + @Test + public void run_allCommands_success() { + HelpCommand command = new HelpCommand(); + command.run(dataManager, moduleManager, ui, recipeManager); + assertFalse(outputStream.toString().isBlank()); + } +} \ No newline at end of file From db5f5e926dfc0a1c4f04b27c7b5e1c69a1d120a8 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 20:54:10 +0800 Subject: [PATCH 353/607] Add RecipeListCommand to help message --- src/main/java/decodex/commands/HelpCommand.java | 4 +++- .../decodex/commands/{ => recipe}/RecipeListCommand.java | 5 ++++- src/test/java/decodex/commands/RecipeListCommandTest.java | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) rename src/main/java/decodex/commands/{ => recipe}/RecipeListCommand.java (91%) diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java index 4e23b3f9da..0dc909b35b 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -1,6 +1,7 @@ package decodex.commands; import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.commands.recipe.RecipeListCommand; import decodex.commands.recipe.RecipeNewCommand; import decodex.commands.recipe.RecipePopCommand; import decodex.commands.recipe.RecipePushCommand; @@ -27,11 +28,12 @@ public class HelpCommand extends Command { private static String[] RECIPE_COMMAND_HELP_MESSAGES = { RecipeNewCommand.COMMAND_HELP_MESSAGE, + RecipeListCommand.COMMAND_HELP_MESSAGE RecipeSelectCommand.COMMAND_HELP_MESSAGE, RecipePushCommand.COMMAND_HELP_MESSAGE, RecipePopCommand.COMMAND_HELP_MESSAGE, RecipeResetCommand.COMMAND_HELP_MESSAGE, - RecipeDeleteCommand.COMMAND_HELP_MESSAGE + RecipeDeleteCommand.COMMAND_HELP_MESSAGE, }; public HelpCommand() { diff --git a/src/main/java/decodex/commands/RecipeListCommand.java b/src/main/java/decodex/commands/recipe/RecipeListCommand.java similarity index 91% rename from src/main/java/decodex/commands/RecipeListCommand.java rename to src/main/java/decodex/commands/recipe/RecipeListCommand.java index d1e752f9e3..4b07a75046 100644 --- a/src/main/java/decodex/commands/RecipeListCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeListCommand.java @@ -1,5 +1,6 @@ -package decodex.commands; +package decodex.commands.recipe; +import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; @@ -15,6 +16,8 @@ public class RecipeListCommand extends Command { public static final String COMMAND_WORD = "list"; + public static final String COMMAND_HELP_MESSAGE = "List modules in recipe\n" + + "Syntax: recipe list {recipeName}"; private static final int ARRAY_INDEX_OFFSET = -1; private final String recipeName; diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index f8a8c5699a..7509107f47 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.commands.recipe.RecipeListCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; From 6fefeac2be8a27a8698185644eab2ce70adf27b8 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 20:56:40 +0800 Subject: [PATCH 354/607] Removed some lines of dead code and the dead readInputFromFile function (initially meant for loading from file name) --- src/main/java/decodex/storage/Storage.java | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 2cfd24a342..6f597e812a 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -78,7 +78,6 @@ public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager, Ui ui) thr if (recipeFiles.length == EMPTY_LENGTH) { return new Recipe[EMPTY_LENGTH]; } - boolean hasError = false; ArrayList tempRecipeList = new ArrayList<>(); for (File recipeFile : recipeFiles) { String recipeFilename = recipeFile.getName(); @@ -161,26 +160,6 @@ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleM return loadedRecipe; } - /** - * Reads and returns the recipe from the provided file. - * - * @param fileName The name of the recipe file specified by the user. - * @return The string formatted recipe from the provided file. - * @throws IOException If an error occurred when reading the file or - * the input file does not exist. - */ - public String readRecipeFromFile(String fileName) throws IOException { - instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); - - File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); - File recipeFile = new File(recipeDirectory, fileName); - Path recipeFilePath = recipeFile.toPath(); - - byte[] recipeContentBytes = readContentFromFile(recipeFilePath); - String recipeContent = convertByteArrayToString(recipeContentBytes); - return recipeContent; - } - /** * Reads and returns the input from the provided file. * From 3b9fa03cfe484c32856be46433087954098fa09c Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 20:58:56 +0800 Subject: [PATCH 355/607] Missing comma --- src/main/java/decodex/commands/HelpCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java index 0dc909b35b..572423b971 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -18,7 +18,7 @@ public class HelpCommand extends Command { public static final String COMMAND_HELP_MESSAGE = "Shows list of available commands\n" + "Syntax: help"; - private static String[] BASIC_COMMAND_HELP_MESSAGES = { + private static final String[] BASIC_COMMAND_HELP_MESSAGES = { HelpCommand.COMMAND_HELP_MESSAGE, ExitCommand.COMMAND_HELP_MESSAGE, InputCommand.COMMAND_HELP_MESSAGE, @@ -26,9 +26,9 @@ public class HelpCommand extends Command { SelectCommand.COMMAND_HELP_MESSAGE }; - private static String[] RECIPE_COMMAND_HELP_MESSAGES = { + private static final String[] RECIPE_COMMAND_HELP_MESSAGES = { RecipeNewCommand.COMMAND_HELP_MESSAGE, - RecipeListCommand.COMMAND_HELP_MESSAGE + RecipeListCommand.COMMAND_HELP_MESSAGE, RecipeSelectCommand.COMMAND_HELP_MESSAGE, RecipePushCommand.COMMAND_HELP_MESSAGE, RecipePopCommand.COMMAND_HELP_MESSAGE, From 31b4b7dc4f6f8e5a957325a35e5183be6fdadbb2 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 21:23:28 +0800 Subject: [PATCH 356/607] Integrate RecipeListCommand --- .../commands/recipe/RecipeListCommand.java | 2 +- .../decodex/parser/RecipeCommandParser.java | 22 +++++++++++++++++++ src/main/java/decodex/ui/Ui.java | 10 ++++----- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/decodex/commands/recipe/RecipeListCommand.java b/src/main/java/decodex/commands/recipe/RecipeListCommand.java index 4b07a75046..cd52a2e145 100644 --- a/src/main/java/decodex/commands/recipe/RecipeListCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeListCommand.java @@ -51,7 +51,7 @@ private String getModuleList(Recipe recipe) { ArrayList recipeModuleList = recipe.getModuleList(); if (recipeModuleList.isEmpty()) { - return RegularMessages.RECIPE_EMPTY; + return " " + RegularMessages.RECIPE_EMPTY; } String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index 51105909cc..6818ee5783 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -9,6 +9,7 @@ import decodex.commands.recipe.RecipePushCommand; import decodex.commands.recipe.RecipeResetCommand; import decodex.commands.recipe.RecipeSelectCommand; +import decodex.commands.recipe.RecipeListCommand; import decodex.data.exception.CommandException; import decodex.ui.messages.ErrorMessages; @@ -63,6 +64,9 @@ public Command parseCommand(String userInput) throws CommandException { case RecipeResetCommand.COMMAND_WORD: recipeCommand = prepareRecipeResetCommand(subCommandTokens); break; + case RecipeListCommand.COMMAND_WORD: + recipeCommand = prepareRecipeListCommand(subCommandTokens); + break; case RecipeDeleteCommand.COMMAND_WORD: recipeCommand = prepareRecipeDeleteCommand(subCommandTokens); break; @@ -167,4 +171,22 @@ private RecipeDeleteCommand prepareRecipeDeleteCommand(String[] subCommandTokens String recipeName = subCommandTokens[SUBARGUMENT_STARTING_INDEX]; return new RecipeDeleteCommand(recipeName); } + + /** + * Prepares and returns the RecipeListCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipeListCommand object. + * @throws CommandException If the number of arguments is invalid. + */ + private RecipeListCommand prepareRecipeListCommand(String[] subCommandTokens) throws CommandException { + if (subCommandTokens.length == SUBCOMMAND_NO_ARGUMENT_LENGTH) { + return new RecipeListCommand(""); + } + if (subCommandTokens.length == SUBCOMMAND_WITH_ARGUMENT_LENGTH) { + String recipeName = subCommandTokens[SUBARGUMENT_STARTING_INDEX]; + return new RecipeListCommand(recipeName); + } + throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); + } } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 70c07c68bb..990b93e6f8 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -148,7 +148,7 @@ public void printModuleRemovedFromRecipe(String moduleName, String recipeName) { assert !moduleName.isBlank() : "Module name should not be empty"; assert recipeName != null : "Recipe name should not be null"; assert !recipeName.isBlank() : "Recipe name should not be empty"; - printSuccess(String.format(RegularMessages.REMOVED_RECIPE_FROM_RECIPE + "\n", moduleName, recipeName)); + printSuccess(String.format(RegularMessages.REMOVED_RECIPE_FROM_RECIPE, moduleName, recipeName)); } /** @@ -162,7 +162,7 @@ public void printModuleAddedToRecipe(String moduleName, String recipeName) { assert !moduleName.isBlank() : "Module name should not be empty"; assert recipeName != null : "Recipe name should not be null"; assert !recipeName.isBlank() : "Recipe name should not be empty"; - printSuccess(String.format(RegularMessages.ADDED_MODULE_TO_RECIPE + "\n", moduleName, recipeName)); + printSuccess(String.format(RegularMessages.ADDED_MODULE_TO_RECIPE, moduleName, recipeName)); } /** @@ -173,7 +173,7 @@ public void printModuleAddedToRecipe(String moduleName, String recipeName) { public void printRecipeSelected(String recipeName) { assert recipeName != null : "Recipe name should not be null"; assert !recipeName.isBlank() : "Recipe name should not be empty"; - printSuccess(String.format(RegularMessages.RECIPE_SELECT + "\n", recipeName)); + printSuccess(String.format(RegularMessages.RECIPE_SELECT, recipeName)); } /** @@ -206,7 +206,7 @@ public void printError(Exception exception) { public void printRecipeDeleted(String recipeName) { assert recipeName != null : "Recipe name should not be null"; assert !recipeName.isBlank() : "Recipe name should not be empty"; - printSuccess(String.format(RegularMessages.RECIPE_DELETED + "\n", recipeName)); + printSuccess(String.format(RegularMessages.RECIPE_DELETED, recipeName)); } /** @@ -217,7 +217,7 @@ public void printRecipeDeleted(String recipeName) { public void printNewRecipeCreated(String recipeName) { assert recipeName != null : "Recipe name should not be null"; assert !recipeName.isBlank() : "Recipe name should not be empty"; - printSuccess(String.format(RegularMessages.NEW_RECIPE_CREATED + "\n", recipeName)); + printSuccess(String.format(RegularMessages.NEW_RECIPE_CREATED, recipeName)); } /** From ff0e64dcf9d43477bcbf7da1a90f289e9d2cc853 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 21:30:48 +0800 Subject: [PATCH 357/607] Removed previously added dummy files. --- input/input.txt | 1 - output/2021-18-10__20.13.32.txt | 1 - 2 files changed, 2 deletions(-) delete mode 100644 input/input.txt delete mode 100644 output/2021-18-10__20.13.32.txt diff --git a/input/input.txt b/input/input.txt deleted file mode 100644 index cef5fdd809..0000000000 --- a/input/input.txt +++ /dev/null @@ -1 +0,0 @@ -TESTING FOR JUNIT diff --git a/output/2021-18-10__20.13.32.txt b/output/2021-18-10__20.13.32.txt deleted file mode 100644 index d785a25b5e..0000000000 --- a/output/2021-18-10__20.13.32.txt +++ /dev/null @@ -1 +0,0 @@ -Hi I am a Test! From 49350f715e7bed08bc713cec93370f4b443e5e06 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 21:33:03 +0800 Subject: [PATCH 358/607] Added logic to collate all formatted failed recipes into a message to print out. --- src/main/java/decodex/Decodex.java | 9 ++++--- src/main/java/decodex/storage/Storage.java | 27 +++++++++++-------- src/main/java/decodex/ui/Ui.java | 6 +++++ .../decodex/ui/messages/ErrorMessages.java | 1 + 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index c3a89e9b43..4fd35c8293 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -50,12 +50,13 @@ private void initDecodex() { parser = new Parser(); ui = new Ui(); storage = new Storage(); + recipeManager = new RecipeManager(); try { - Recipe[] savedRecipes = loadSavedRecipes(); + loadSavedRecipes(); } catch (IOException err) { ui.printError(err); } - recipeManager = new RecipeManager(); + } /** @@ -85,7 +86,7 @@ public void run() { } while (!(command instanceof ExitCommand)); } - private Recipe[] loadSavedRecipes() throws IOException { - return storage.loadRecipesFromDirectory(moduleManager, ui); + private void loadSavedRecipes() throws IOException { + storage.loadRecipesFromDirectory(moduleManager, recipeManager, ui); } } diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 6f597e812a..3d1d68fd8b 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -2,9 +2,11 @@ import decodex.data.exception.ModuleException; import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.modules.Module; import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -65,34 +67,37 @@ public Storage() { * Loads all the recipes previously saved in the recipe directory. * * @param moduleManager The ModuleManager object. - * @param ui The Ui object. - * @return The list of Recipe objects. + * @param recipeManager The RecipeManager object. + * @param ui The Ui object. * @throws IOException If an error occurred when reading the file. * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. */ - public Recipe[] loadRecipesFromDirectory(ModuleManager moduleManager, Ui ui) throws IOException { + public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager recipeManager, Ui ui) + throws IOException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); File[] recipeFiles = getAllRecipeFiles(); if (recipeFiles.length == EMPTY_LENGTH) { - return new Recipe[EMPTY_LENGTH]; + return; } - ArrayList tempRecipeList = new ArrayList<>(); + ArrayList errorsOfFailedRecipeLoads = new ArrayList<>(); for (File recipeFile : recipeFiles) { String recipeFilename = recipeFile.getName(); String recipeName = recipeFilename.split(FILENAME_EXTENSION_SPLIT_REGEX)[RECIPE_NAME_INDEX]; try { Recipe recipe = readRecipeFromFile(recipeName, recipeFile, moduleManager); - tempRecipeList.add(recipe); - } catch (IOException | ModuleException | ModuleManagerException err) { - ui.printError(err); + recipeManager.addRecipe(recipe); + } catch (IOException | ModuleException | ModuleManagerException | RecipeManagerException err) { + String failedToLoadMessage = recipeName + ErrorMessages.FAILED_TO_LOAD_RECIPE_FILE_MESSAGE; + errorsOfFailedRecipeLoads.add(failedToLoadMessage); } } - int tempRecipeListSize = tempRecipeList.size(); - Recipe[] recipeList = tempRecipeList.toArray(new Recipe[tempRecipeListSize]); - return recipeList; + if (errorsOfFailedRecipeLoads.isEmpty()) { + return; + } + ui.printFailedToLoadFromStorageMessage(errorsOfFailedRecipeLoads); } /** diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 1237d4f2c8..250b5c4c20 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -206,6 +206,12 @@ public void printNewRecipeCreated(String recipeName) { printSuccess(String.format(RegularMessages.NEW_RECIPE_CREATED + "\n", recipeName)); } + public void printFailedToLoadFromStorageMessage(ArrayList failedFilesMessageList) { + for (String failedFileMessage : failedFilesMessageList) { + System.out.println(failedFileMessage); + } + } + /** * Prints the prompt header. */ diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 019c9116ae..96dc6dada6 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -29,4 +29,5 @@ public class ErrorMessages { public static final String FILE_READ_ERROR_MESSAGE = "Something went wrong when reading from the file"; public static final String DIRECTORY_INSTANTIATION_FAILED_MESSAGE = "Failed to create the directory for "; public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; + public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = " failed to be loaded into Decodex"; } From 2fe095db02068566f59981e27acef82738241b0d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Tue, 26 Oct 2021 21:35:05 +0800 Subject: [PATCH 359/607] Added some ignoring for input output folders. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 04563a1a3f..175b9ea3d6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,9 @@ bin/ # Recipe files /recipe/* +# Input, Output files +/input/* +/output/* + /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT From f236f6c2d7541450c5a76162b438a6c9cd59538c Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 26 Oct 2021 21:35:35 +0800 Subject: [PATCH 360/607] Update text-ui --- src/main/java/decodex/commands/recipe/RecipeListCommand.java | 2 +- text-ui-test/EXPECTED.TXT | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/decodex/commands/recipe/RecipeListCommand.java b/src/main/java/decodex/commands/recipe/RecipeListCommand.java index cd52a2e145..4b07a75046 100644 --- a/src/main/java/decodex/commands/recipe/RecipeListCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeListCommand.java @@ -51,7 +51,7 @@ private String getModuleList(Recipe recipe) { ArrayList recipeModuleList = recipe.getModuleList(); if (recipeModuleList.isEmpty()) { - return " " + RegularMessages.RECIPE_EMPTY; + return RegularMessages.RECIPE_EMPTY; } String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 0531722f7d..b726f771c3 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -25,18 +25,14 @@ You do not have any recipes Decodex > [x] The given recipe could not be found. Decodex > [+] Recipe testRecipe created successfully - Decodex > Here is the list of available recipes: testRecipe Decodex > [+] There are no modules in the recipe [+] Output: "I am a lab rat" Decodex > [+] Added hexencode module to recipe testRecipe - Decodex > [+] Added base64encode module to recipe testRecipe - Decodex > [+] Added rotencode module to recipe testRecipe - Decodex > [+] Modules executed: hexencode, base64encode, rotencode [+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" Decodex > [x] Unknown command, please enter a valid command From 6a87ad3f8fc7534cd03af9d445894d43e823105c Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 27 Oct 2021 00:31:15 +0800 Subject: [PATCH 361/607] Update puml --- .../BasicCommandClassDiagram.puml | 45 +++++++++++++++++ .../classdiagrams/CommandClassDiagram.puml | 38 --------------- docs/puml/classdiagrams/DataClassDiagram.puml | 8 ++-- .../classdiagrams/ModuleClassDiagram.puml | 19 ++++---- .../ModuleManagerClassDiagram.puml | 6 ++- .../classdiagrams/ParserClassDiagram.puml | 15 ++++-- .../RecipeCommandClassDiagram.puml | 48 +++++++++++++++++++ docs/puml/classdiagrams/UiClassDiagram.puml | 28 +++++++++++ docs/puml/classdiagrams/colors.puml | 8 ++++ .../ExecutionOfModuleSequenceDiagram.puml | 1 + .../ExecutionOfRecipeSequenceDiagram.puml | 7 ++- .../commands/ListCommandSequenceDiagram.puml | 10 ++-- .../RecipeListCommandSequenceDiagram.puml | 4 +- 13 files changed, 174 insertions(+), 63 deletions(-) create mode 100644 docs/puml/classdiagrams/BasicCommandClassDiagram.puml delete mode 100644 docs/puml/classdiagrams/CommandClassDiagram.puml create mode 100644 docs/puml/classdiagrams/RecipeCommandClassDiagram.puml create mode 100644 docs/puml/classdiagrams/UiClassDiagram.puml create mode 100644 docs/puml/classdiagrams/colors.puml diff --git a/docs/puml/classdiagrams/BasicCommandClassDiagram.puml b/docs/puml/classdiagrams/BasicCommandClassDiagram.puml new file mode 100644 index 0000000000..a324436dfb --- /dev/null +++ b/docs/puml/classdiagrams/BasicCommandClassDiagram.puml @@ -0,0 +1,45 @@ +@startuml +!include colors.puml + +skinparam classAttributeIconSize 0 +hide circle + +abstract class "Command\n{abstract}" as Command LOGIC_COLOR { + {abstract} +run(dataManager: DataManager, moduleManager: ModuleManager, ui: Ui, recipeManager: RecipeManager): void +} + +Command <|-left- ExitCommand +Command <|-down- InputCommand +Command <|-down- ListCommand +Command <|-right- ResetCommand +Command <|-up- SelectCommand +Command <|-up- HelpCommand + +class ExitCommand LOGIC_COLOR { +} + +note "run() method omitted \nfrom inheriting classes" as omitMessage + +class InputCommand LOGIC_COLOR { + - dataString: String +} + +class ListCommand LOGIC_COLOR { + - listCategory: String +} + +class ResetCommand LOGIC_COLOR { +} + +class SelectCommand LOGIC_COLOR { + - selectCategory: String + - itemName: String + - parameters: String[] +} + +class HelpCommand LOGIC_COLOR { + - {static} BASIC_COMMAND_HELP_MESSAGES: String[] + - {static} RECIPE_COMMAND_HELP_MESSAGES: String[] +} + +@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/CommandClassDiagram.puml b/docs/puml/classdiagrams/CommandClassDiagram.puml deleted file mode 100644 index a00a58294a..0000000000 --- a/docs/puml/classdiagrams/CommandClassDiagram.puml +++ /dev/null @@ -1,38 +0,0 @@ -@startuml -skinparam classAttributeIconSize 0 -hide circle - -abstract class "Command\n{abstract}" as Command { - {abstract} +run(dataManager: DataManager, moduleManager: ModuleManager, ui: Ui, recipeManager: RecipeManager): void -} - -Command <|-- ExitCommand -Command <|-- InputCommand -Command <|-- ListCommand -Command <|-- ResetCommand -Command <|-- SelectCommand -Command <|-- HelpCommand - -class ExitCommand { -} - -note "run() method omitted \nfrom inheriting classes" as omitMessage - -class InputCommand { - - dataString: String -} - -class ListCommand { -} - -class ResetCommand { -} - -class SelectCommand { - - moduleName: String -} - -class HelpCommand { -} - -@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/DataClassDiagram.puml b/docs/puml/classdiagrams/DataClassDiagram.puml index 16db42cb23..371b504947 100644 --- a/docs/puml/classdiagrams/DataClassDiagram.puml +++ b/docs/puml/classdiagrams/DataClassDiagram.puml @@ -2,19 +2,21 @@ skinparam classAttributeIconSize 0 hide circle +!include colors.puml + Decodex -> "1" DataManager DataManager ---> "0..1 \nOriginal" Data DataManager ---> "0..1 \nCurrent" Data -class Decodex { +class Decodex DECODEX_COLOR { - initDecodex(): void + run(): void + {static} main(): void } -class DataManager { +class DataManager DATA_COLOR { + getOriginalData(): Data + getCurrentData(): Data + setOriginalData(originalData: Data): Data @@ -22,7 +24,7 @@ class DataManager { + resetToOriginalData(): void } -class Data { +class Data DATA_COLOR { - rawBytes: byte[] + getRawBytes(): byte[] } diff --git a/docs/puml/classdiagrams/ModuleClassDiagram.puml b/docs/puml/classdiagrams/ModuleClassDiagram.puml index b8add9c95e..8c04399e00 100644 --- a/docs/puml/classdiagrams/ModuleClassDiagram.puml +++ b/docs/puml/classdiagrams/ModuleClassDiagram.puml @@ -2,7 +2,9 @@ skinparam classAttributeIconSize 0 hide circle -abstract class "Module\n{abstract}" as Module { +!include colors.puml + +abstract class "Module\n{abstract}" as Module MODULE_COLOR { # name: String # description: String {abstract} +run(data: Data): Data @@ -16,34 +18,33 @@ Module <|-down- BinaryEncoder Module <|-down- BinaryDecoder Module <|-up- RotEncoder -class Base64Encoder { +class Base64Encoder MODULE_COLOR { +run(data: Data): Data } -class Base64Decoder { +class Base64Decoder MODULE_COLOR { +run(data: Data): Data } -class HexEncoder { +class HexEncoder MODULE_COLOR { +run(data: Data): Data } -class HexDecoder { +class HexDecoder MODULE_COLOR { +run(data: Data): Data } -class BinaryEncoder { +class BinaryEncoder MODULE_COLOR { +run(data: Data): Data } -class BinaryDecoder { +class BinaryDecoder MODULE_COLOR{ +run(data: Data): Data } -class RotEncoder { +class RotEncoder MODULE_COLOR { - rotateOffset: int +run(data: Data): Data } - @enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml b/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml index c452280528..1efa4b8bce 100644 --- a/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml +++ b/docs/puml/classdiagrams/ModuleManagerClassDiagram.puml @@ -2,16 +2,18 @@ skinparam classAttributeIconSize 0 hide circle +!include colors.puml + Decodex -> "1" ModuleManager -class Decodex { +class Decodex DECODEX_COLOR { - initDecodex(): void + run(): void + {static} main(): void } -class ModuleManager { +class ModuleManager MODULE_COLOR { - {static} MODULE_LIST: String[][] + getModules(): String[][] + selectModule(moduleName: String, parameters: String[]): Module diff --git a/docs/puml/classdiagrams/ParserClassDiagram.puml b/docs/puml/classdiagrams/ParserClassDiagram.puml index 24a64165ac..3ea699124a 100644 --- a/docs/puml/classdiagrams/ParserClassDiagram.puml +++ b/docs/puml/classdiagrams/ParserClassDiagram.puml @@ -2,20 +2,27 @@ skinparam classAttributeIconSize 0 hide circle -Decodex -> "1" Parser +!include colors.puml +Decodex -> "1" Parser +Parser -> "1" RecipeCommandParser +Parser <|- RecipeCommandParser -class Decodex { +class Decodex DECODEX_COLOR { - initDecodex(): void + run(): void + {static} main(): void } -class Parser { +class Parser LOGIC_COLOR { + getCommandType(userInput: String): String - + getModuleName(userInput: String): String + getInputString(userInput: String): String + parseCommand(userInput: String): Command + # getTokens(userInput: String): String[] +} + +class RecipeCommandParser LOGIC_COLOR { + + parseCommand(userInput: String): Command } @enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/RecipeCommandClassDiagram.puml b/docs/puml/classdiagrams/RecipeCommandClassDiagram.puml new file mode 100644 index 0000000000..4b94260593 --- /dev/null +++ b/docs/puml/classdiagrams/RecipeCommandClassDiagram.puml @@ -0,0 +1,48 @@ +@startuml +skinparam classAttributeIconSize 0 +hide circle + +!include colors.puml + +abstract class "Command\n{abstract}" as Command LOGIC_COLOR { + {abstract} +run(dataManager: DataManager, moduleManager: ModuleManager, ui: Ui, recipeManager: RecipeManager): void +} + +Command <|-down- RecipeNewCommand +Command <|-down- RecipeListCommand +Command <|-down- RecipeDeleteCommand +Command <|-up- RecipePushCommand +Command <|-left- RecipePopCommand +Command <|-right- RecipeResetCommand +Command <|-up- RecipeSelectCommand + +note "run() method omitted \nfrom inheriting classes" as omitMessage + +class RecipeNewCommand LOGIC_COLOR { + - recipeName: String +} + +class RecipeListCommand LOGIC_COLOR { + - recipeName: String +} + +class RecipeDeleteCommand LOGIC_COLOR { + - recipeName: String +} + +class RecipePushCommand LOGIC_COLOR { + - moduleName: String + - parameters: String[] +} + +class RecipePopCommand LOGIC_COLOR { +} + +class RecipeResetCommand LOGIC_COLOR { +} + +class RecipeSelectCommand LOGIC_COLOR { + - recipeName: String +} + +@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/UiClassDiagram.puml b/docs/puml/classdiagrams/UiClassDiagram.puml new file mode 100644 index 0000000000..191cb6da14 --- /dev/null +++ b/docs/puml/classdiagrams/UiClassDiagram.puml @@ -0,0 +1,28 @@ +@startuml +skinparam classAttributeIconSize 0 +hide circle + +!include colors.puml + +Decodex -> "1" Ui + +class Decodex DECODEX_COLOR { + - initDecodex(): void + + run(): void + + {static} main(): void +} + + +class Ui UI_COLOR { + - in: Scanner + + readInput(): String + + printSuccess(): void + + printError(): void +} + +note right + Only the commonly used + methods are listed. +end note + +@enduml \ No newline at end of file diff --git a/docs/puml/classdiagrams/colors.puml b/docs/puml/classdiagrams/colors.puml new file mode 100644 index 0000000000..1d72de5828 --- /dev/null +++ b/docs/puml/classdiagrams/colors.puml @@ -0,0 +1,8 @@ +!define DECODEX_COLOR #99D5CA +!define UI_COLOR #FFC374 +!define LOGIC_COLOR #FF8F80 +!define DATA_COLOR #F9D2DE +!define RECIPE_COLOR #FFDF71 +!define MODULE_COLOR #C7E8AC +!define BLUE_COLOR #99D2F2 +!define STORAGE_COLOR #99D2F2 \ No newline at end of file diff --git a/docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml index 64829184fe..14f5b05e6f 100644 --- a/docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/ExecutionOfModuleSequenceDiagram.puml @@ -45,6 +45,7 @@ else itemName is not blank activate Ui SelectCommand <-- Ui deactivate Ui + SelectCommand <-[hidden]- Ui destroy Module <-- SelectCommand diff --git a/docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml index 70fe0c6383..04ce891784 100644 --- a/docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/ExecutionOfRecipeSequenceDiagram.puml @@ -28,7 +28,7 @@ else itemName is not blank SelectCommand -> DataManager: getCurrentData() activate DataManager - SelectCommand <-- DataManager: :DataManager + SelectCommand <-- DataManager: :Data deactivate DataManager SelectCommand -> Recipe: run(:Data) @@ -46,6 +46,11 @@ else itemName is not blank SelectCommand <-- Ui deactivate Ui + SelectCommand -> Ui: printOutput(:String) + activate Ui + SelectCommand <-- Ui + deactivate Ui + <-- SelectCommand end diff --git a/docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml index dc8eef5b7c..4dbbd12452 100644 --- a/docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/ListCommandSequenceDiagram.puml @@ -10,7 +10,7 @@ end box -> ListCommand: run() activate ListCommand -alt isPrintModuleList +opt isPrintModuleList ListCommand -> ListCommand: getModuleList(:ModuleManager) activate ListCommand ListCommand --> ListCommand @@ -19,8 +19,8 @@ alt isPrintModuleList activate Ui ListCommand <-- Ui deactivate Ui - <-- ListCommand -else isPrintRecipeList +end +opt isPrintRecipeList ListCommand -> ListCommand: getRecipeList(:RecipeManager) activate ListCommand ListCommand --> ListCommand @@ -29,8 +29,10 @@ else isPrintRecipeList activate Ui ListCommand <-- Ui deactivate Ui +end +alt isPrintModuleList or isPrintRecipeList <-- ListCommand -else not isPrintModuleList and not PrintRecipeList +else not isPrintModuleList and not isPrintRecipeList <-- ListCommand: CommandException end diff --git a/docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml b/docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml index fdff2c1b7d..3f58a3d302 100644 --- a/docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml +++ b/docs/puml/sequencediagrams/commands/RecipeListCommandSequenceDiagram.puml @@ -26,9 +26,9 @@ else recipeName is not blank deactivate RecipeManager end -RecipeListCommand -> RecipeListCommand: getModuleList(:String) +RecipeListCommand -> RecipeListCommand: getModuleList(:Recipe) activate RecipeListCommand -RecipeListCommand --> RecipeListCommand +RecipeListCommand --> RecipeListCommand: :String deactivate RecipeListCommand RecipeListCommand -> Ui: printRecipeModulesList(:String, :String) From a10ccabefc8c197e337977a400faa0790b9e9615 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 27 Oct 2021 00:57:58 +0800 Subject: [PATCH 362/607] Add DG images --- docs/images/dg/CommandClass1.png | Bin 0 -> 28394 bytes docs/images/dg/CommandClass2.png | Bin 0 -> 27565 bytes docs/images/dg/DataClass.png | Bin 0 -> 15424 bytes docs/images/dg/DataComponent.png | Bin 0 -> 5431 bytes docs/images/dg/DecodexInitSeq.png | Bin 0 -> 45594 bytes docs/images/dg/DecodexMainSeq.png | Bin 0 -> 30371 bytes docs/images/dg/ExitCommand.png | Bin 0 -> 4649 bytes docs/images/dg/InputCommand.png | Bin 0 -> 22812 bytes docs/images/dg/ListCommand.png | Bin 0 -> 28706 bytes docs/images/dg/LogicComponent.png | Bin 0 -> 31557 bytes docs/images/dg/ModuleClass.png | Bin 0 -> 24777 bytes docs/images/dg/ModuleComponent.png | Bin 0 -> 14996 bytes docs/images/dg/ModuleManagerClass.png | Bin 0 -> 18214 bytes docs/images/dg/ParserBasicSeq.png | Bin 0 -> 56714 bytes docs/images/dg/ParserRecipeSeq.png | Bin 0 -> 42571 bytes docs/images/dg/RecipeClass.png | Bin 0 -> 51557 bytes docs/images/dg/RecipeComponent.png | Bin 0 -> 9530 bytes docs/images/dg/RecipeDeleteCommand.png | Bin 0 -> 17233 bytes docs/images/dg/RecipeListCommand.png | Bin 0 -> 24214 bytes docs/images/dg/RecipeNewCommand.png | Bin 0 -> 26967 bytes docs/images/dg/RecipePopCommand.png | Bin 0 -> 18148 bytes docs/images/dg/RecipePushCommand.png | Bin 0 -> 30417 bytes docs/images/dg/RecipeResetCommand.png | Bin 0 -> 14033 bytes docs/images/dg/RecipeSelectCommand.png | Bin 0 -> 18131 bytes docs/images/dg/ResetCommand.png | Bin 0 -> 9779 bytes docs/images/dg/SelectCommand.png | Bin 0 -> 33343 bytes docs/images/dg/SelectCommandModule.png | Bin 0 -> 59295 bytes docs/images/dg/SelectCommandRecipe.png | Bin 0 -> 34368 bytes docs/images/dg/StorageClass.png | Bin 0 -> 31418 bytes docs/images/dg/UiClass.png | Bin 0 -> 9482 bytes docs/images/dg/architecture.png | Bin 0 -> 87577 bytes 31 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/dg/CommandClass1.png create mode 100644 docs/images/dg/CommandClass2.png create mode 100644 docs/images/dg/DataClass.png create mode 100644 docs/images/dg/DataComponent.png create mode 100644 docs/images/dg/DecodexInitSeq.png create mode 100644 docs/images/dg/DecodexMainSeq.png create mode 100644 docs/images/dg/ExitCommand.png create mode 100644 docs/images/dg/InputCommand.png create mode 100644 docs/images/dg/ListCommand.png create mode 100644 docs/images/dg/LogicComponent.png create mode 100644 docs/images/dg/ModuleClass.png create mode 100644 docs/images/dg/ModuleComponent.png create mode 100644 docs/images/dg/ModuleManagerClass.png create mode 100644 docs/images/dg/ParserBasicSeq.png create mode 100644 docs/images/dg/ParserRecipeSeq.png create mode 100644 docs/images/dg/RecipeClass.png create mode 100644 docs/images/dg/RecipeComponent.png create mode 100644 docs/images/dg/RecipeDeleteCommand.png create mode 100644 docs/images/dg/RecipeListCommand.png create mode 100644 docs/images/dg/RecipeNewCommand.png create mode 100644 docs/images/dg/RecipePopCommand.png create mode 100644 docs/images/dg/RecipePushCommand.png create mode 100644 docs/images/dg/RecipeResetCommand.png create mode 100644 docs/images/dg/RecipeSelectCommand.png create mode 100644 docs/images/dg/ResetCommand.png create mode 100644 docs/images/dg/SelectCommand.png create mode 100644 docs/images/dg/SelectCommandModule.png create mode 100644 docs/images/dg/SelectCommandRecipe.png create mode 100644 docs/images/dg/StorageClass.png create mode 100644 docs/images/dg/UiClass.png create mode 100644 docs/images/dg/architecture.png diff --git a/docs/images/dg/CommandClass1.png b/docs/images/dg/CommandClass1.png new file mode 100644 index 0000000000000000000000000000000000000000..1720a7d1399a15abd00705aede3f3b217c0624da GIT binary patch literal 28394 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 C@PlIj literal 0 HcmV?d00001 diff --git a/docs/images/dg/CommandClass2.png b/docs/images/dg/CommandClass2.png new file mode 100644 index 0000000000000000000000000000000000000000..2f881974ecbcda2844f6eae97c1359ad05faa9a3 GIT binary patch literal 27565 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 fhhEV8;f3u$EOtf)V|UVVZ|*qO literal 0 HcmV?d00001 diff --git a/docs/images/dg/DataClass.png b/docs/images/dg/DataClass.png new file mode 100644 index 0000000000000000000000000000000000000000..80e8c04c58cd16d6ce63d9dea08fb42f700acd0c GIT binary patch literal 15424 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/DataComponent.png b/docs/images/dg/DataComponent.png new file mode 100644 index 0000000000000000000000000000000000000000..d05e86e3e67f3ed0cbf685116a3b8b3e6b55112b GIT binary patch literal 5431 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 Yj{L^~RV2bqVF2K=rJY6Xh1=Nw0^nn}zW@LL literal 0 HcmV?d00001 diff --git a/docs/images/dg/DecodexInitSeq.png b/docs/images/dg/DecodexInitSeq.png new file mode 100644 index 0000000000000000000000000000000000000000..ed3b03688d5d4d08fb88c3a43062ca9a3e541e98 GIT binary patch literal 45594 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%0AuCTSzs3iZU;%pF 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 T5X&P3pQ|3!R8Bf@#^e71wSxkC literal 0 HcmV?d00001 diff --git a/docs/images/dg/DecodexMainSeq.png b/docs/images/dg/DecodexMainSeq.png new file mode 100644 index 0000000000000000000000000000000000000000..c45bf09960cfdaad494971efe22f801c800fdac2 GIT binary patch literal 30371 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/ListCommand.png b/docs/images/dg/ListCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..6d07c1706ccb61ee024f81093ef935c5f9b817e1 GIT binary patch literal 28706 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/ModuleManagerClass.png b/docs/images/dg/ModuleManagerClass.png new file mode 100644 index 0000000000000000000000000000000000000000..16c5f1215672702232a7dcf4ec8cf247d84b7825 GIT binary patch literal 18214 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 JM9RSb{{Yg+F=GG# literal 0 HcmV?d00001 diff --git a/docs/images/dg/ParserBasicSeq.png b/docs/images/dg/ParserBasicSeq.png new file mode 100644 index 0000000000000000000000000000000000000000..a65aef5f27fe01874838caca386ecba6e7f71c38 GIT binary patch literal 56714 zcmb@u1z40@_dX1QfKo$9$AFX|D2;T3AV`Cxlyrl1N;e`QNP~1q3?LyPsS?sdBTAQa z{`Y|Ayyt!2-}nE0*LQvA;#|VaGtaa4+H0+SuY28l!ju$cuy2#yMnXcuek?1gf`o(u zMMAn!je!PUIo*C1hJ*w~dMqiX=Jswq&DD!=#jm)(?#x)DBv&%ZWudUnzMwV{d? z&LHqO{nx%n%j*fJpYJ%$T!nuL3OK#Eq-F}wzWynCeAz#Oo%!eQqRmjWz(4O4SQ21k zNGTIu{fsb>LH_=T0vGi9RYEECRC7_L-rIKyUt`b&Lc~K!t?|Hzo@LRhMn+tK!Hx~ye+>1+>V{!D*<1IjsdOE^RF3XLzabtfIA14fdE+3`7~G%U zf1MrL_5O*=@p*FY$-TGypO@k({&9&EGZ>I#L%7-jc`U9b;rti>jod`l#>Vc=PYM`? z2(9THL*Otr z9~3-P@*v`a$lY@Y3Ykesb{dCI#bu{nlJG^o@Ti zM1^+_3{q2~z~RI>N)u?K^Jn*f{t=de`u*r5T?<+`or(@eD!qw$LLI=3C6B$&eH zfFDG>s4d+=OoRhY~FZf?hp?C+nDhHu+*uZ|sxM-Z`( zH+zTmEmo^79~vl7$ygvpUnGwv>3h0b^UBRpfej63eSNpKhms5;E~`FnCAtUOvWQt+ zKlF@?V@ERXI&gJg=gciRgQW1El$;C+O*MaNBB%%I4qQ<;YaIDO!rtR`Tqw!&A>GBX`F@ zv_Oc;ozY0l(T=5-3;wWKA5UszO4#IXJIFFAu z*U7VAT2E$c2v=nXXp9X2eg8d;5G}^E2$3rAw^sN2v%B!HXjb-4-7b z&2q{Pkqh=)Sy{}=q!J+qpZ3>6cG_`Ix5o?^vy|NCfNi$Bs2MA~Pw^$Cd4`vKf4&W= zwA>e*YCQHuxJQYBfuDNuuV?q4Mqhggsir7%y%P)-j2l>HYHDY^t`Y*XrEH&caW@~m zKV(7~0EP0@-V#1`CN47&iG26YA(_LpU}T@mY7~`GvGMtbuf7+@m7ABJBl2?!%+YM8 z_yWjzqn{6E-;bo2&>4=Vk?@$?T{1$L$Ik947WE7!Vh+)i@?f~*31cbjCElkRM~5v2 zZ5h5k*43B0OI5KMBFJOq)SXENn_^yGY6)n$?;~@r!e>Ms#j$i9O2pdR*75k&mrbYB z1NJG`S?_EP#dI&_H!^v*KXpb0F&sWsjBZb0p$^yQq#(iWi@(bS4U8?a4@R!{*nlc# z%!DCkA?O}DQpv?r8>ZY)$d4-a$*R0`)aLSNQ5ejuW7WgUtI?r!{dq#yNXavT!G=)G zmd1$CP$gx7Z|~d{c)wu;_)+^#nbm$B{aNz@OTgPBf8yftlgQ6cMVboXGcCSpQYb3% zoyu1czWN%uu;c4jY)cnNXY__$^+09a5$TT~k?o)S(+2A*JDHAV8t9vKTg>_wA*jd> z4sqRIDPu$~__E|NF}8WNO;$EGEGh*biKB>P6y7$wFHVe#jE9$%%xialbBeW+KheX} zGu)`$kn-fxpjo#B-VB!W;-Xn&Y;g@PI?U$e=I$PDOVE%nWJ`F8ZFhI5CBSI>h1GB1 zlBBf;BGX-6*k_aWID&rgNoojRN-R^$NS)PSh-lIiI>EojC zJRcw7qki8KPUuPe5{Saqt}ghh2R?rG&W39_d6T||4~7nmZODctp_%r({V&^FQ&Ur4 zI(_8fwLfaiu&PW%fd^WTkQBt|c3oT6T=YV^6bE0=M;I)CLhGSc&+lx(Tek-JPS5p2 zQ1Xkjz)qtU#A(~w;Qsq^piuS0ps`JHcwS|t!WZG%v4_FF+pQulaI==#h>D8m#cMqv zuDhsGjrUH#e3!@BpTVZHr6LDeweZ`1_&lG=H!$eZNi((gP4ZwKh7DAmqpEt?JFiW{XCCZ*RB9D}(70 zKWGl|Ju`l79*fu%hl$?RV4QbRAJWj$5*qlP7mZX_XF>_!um`QBI$aemYqTAo-x>s_ zzab2Vd0;+x*QB`U7l<(~8#4bqd9#JHAeh7cq?q;08Ad?B4JF;PNVeHk zMyE)kEMB{r;dKXrKEH9!;FXqxFPyl>tpSB~4vQGg=bJ9Mxm>UbziyYm^K)!bK<&L5 zPrJJ*+*UWJsqH$tWDI}*ETR^3+n##?{%CE_N#A@-m0)ps+<(i&EJj>O3F++2YoN+Z z;Oy+T4iCDbCfGp{PAeRIxIdO539n^!fvKzCk+%bB;^JJO6(n(1?T)#loehRH;QOh( zA~!%z1c3s!C*}r66?Gxsa~J&CG;frZ6+Ju(3Jil;G+BB1Pw=f0;-%w$U&ZI0k$4yw z+cM8H@ln2iTqb(0^Bwnq4C2}Ouzd`qi2!ZNz|2gf65U7e7bvu24sQ^f>HA4b(a_(S z2;Y!WAR)HwO%m=n{`XB>47_eGy2|$No3;``=S{NM2F(BWoAm!@?BLkpNBH1D8}F7a zW6isTB`FY6kcj_)GJ(Ua<^aF^1i-=a24bWHcM7d}p|fngVm-t6TwW8y_nh3xbw9%* z9Cd|zXac+A8wA;8rSz!masfKH21G(=xoqthv|y{Gv~P?8_vdk8RCNOpq zw~O3e6yyW^P~>k1qCZx70B|cj|6ecV(O=^pUa|p$Rymz_yNip!LpK2U0T?ha8G&f% zAlTP1(c*u9YaEUECO&v94&O{S3L>M5azUffDGz7>j5KSnl%l#ql}3I}g3A@Sw`cW1O=#^$3p;mmE8zL>*zT-Y`Ki067u*oJ4+o!#@$w=UaUJ! zm;V@oI(Tq#ema;ZPnz-7Mera2dx^*s&s=o9?Myar$7uRj_xFfGtYHumV)A>?X z>gTzQGox;1c}yNex7WJ(r86WSjjdg+lk9ba(0M z*$<|Dv9Ym8NaHnD#DuqRHa0cIy?YmVIO}VW1S=`QIzQQerBW!FA?&TBuU{q~5gy*w z6HiTEv(|EXUcPzRm-HaVtUq0eIbnICim^<$p4PIfGm1P*r7%;aaC=Dpf#KFn69zVR zka*Uo&FPODy*eY6<$5296jHd@C%0g;J`!Eglv!%bm0+lYsNTJA?rBvT1r<=VA7-l@ z9OTcRW*fZEd#Q6mk!+O@#o+%^$oy)k_rSG*PHC69~PR?TunYO))aYLhAKPrOw!3`u}D#Y(1 zmuCb5FV{s)Omf+_h6`04PWCL$cDktDRtxhV8h2vpX#)dF5tyr{@HxA4s4A3Tm{maf6C9adMh?lM`Jzt7a<*{$YNU-LJf}NCkC6nHt3kKfhbnNE19gx9izz zx%AmvpCGVnK4!3;ZlIs!rKP>$d$xm)G`H|M!hEW(lI?3(7dp&twsyQzI*M$i%;QZ` zl0?p6vUL}=?-{Zf#Qu2KRJlRO-R9BD^Xh>CC9tXWBl&@XOYsd61mgGM14q^ViX9xD zkvOTLo>JfM{{{^e>EdMV0fn#^$+RH1Il(k4)#j#~Q3ulWEwf&~cSjk=51+u^yvI>m z_#Wh>)Zi$L^HYH!&%4$f=Z!G7E|o3?Ha`U&2>HJq!1Hhuxghz?nlFpb$`Cyv5mB~5 zi;wwmLEK=6>LpJhSoUlicAn~4gBQR-8Ik7tMJ~Bce(JYSsw*Uxeg*EI3hxKLs9j2gWWO~dlv6FYsHFY~_t5 zWmhLrlZ6}wll>!Ms|XajpqZ=;1SME%02Ppa)49N1M~6$*ka@8$da{B{t*Kh;L=yq@(r#hz|(kq{L{+9W_NI6Hq>E5Y3SETvdr z-lHZ|!ux5EEnFYP5p065?~>DSiH33K3`xOsmku_Oaj6W11<|)~NTWD0o#<}y zBk&g3`YTvi~-aJVQ=xWn8_S81; z;<8Cm{j=pEHqxwh4PYRUNq?@sAbE_Ffl|oWFXq!c{$>Z-3_zN6{^&9p^{b(T-#e?3 zJE54krK8YY=?p_HqSpa=t&g?fxhOLf4~G`i@c`|D0Z1bNrYB0r416im_+1=bI3wvo z`u&IFf4LZ=0X_htPemnbHAy1#OC%87a*)61R_zlKjSYVj%~SLWwS>U@l$UKk1F5t~ z6JHz@Vf;y;fA;0@*Ac4=Kit8UO1X04am=@@$Xq{-YBW{6cRazc5Q-WTu}HIU57y;p3# z?NZe3cOP4^)7a3$tseed9{_O3f+I)|*PzaY^%H4IOkV4~aT54V!1q-OEDzTR(*;6k zg(!4>AS2kh%m%`%TYZQnBrJR#S@x0--+5I90ow|x>M-=KfnX6!{IA!Asui?cBkLnL zG69NuD+sWcTPB9Uu?1{T(vRhr5d>r<`u_mx5YUvd|1WrXwTb{9_Eh}~N8&a7!HoZW z@qY!Eh2Mk)0Zhsn!}*dL8C|Koy}v8&ri4UAHSm4O``3L`v4L~$kT?4xsgEc&jA)C` z-HLzzHpWDR=)BEqyzIS@>~sOzfL9N5JY~FE1ZP+IDTF#}2Nkxhe{NfPKE-gjLy@s` zWg{pWU)L)|mjgop@yt(;+BxyaGCOwBPv>VNe$8(@iiZ%q?+oy-X%9?JDUlO;JjPgN zNHUYm-I~|6_5@2rq;@Driu22+i-@ouANuTr{;tBC6C`k&J8utR4%(FJJDnxBLaj(N zY_@LElm#qS{R2LO8FIbLjJ8Y)cqSwertb+LU>5)+yb>rWwH|&g%OI)b~8u9uBb1mE;NN zoyEn)UA~u6?cHq7*3j7nQaQO^O4nS(XD8(b$L1b*G#NU&p7BD z@cr99Gy32kvJ63fog@Z~8;Lk85uygvs3#}S+sA3fISfg}-&}0lyLJI+0#9)5->WpA zV4}bl6atp-`jB-lxg?k}gNfHFCVdJviKy!LQS-kxfvBPvX7^&W>n;ImQy^B%acpW!eo;u;N4;zajMa6Y>b#r%v($G>o zP^2_fNAPUr2AUm~!_ubOtBmqpCS_@(Q?(ii?&f4<0X=E(Fxz^Lso4q~&zQlXN3X}q z$)#)LUs`xU0ag8{gyN%eUSDYk89jNnML<}G3mxS(fnNRklqCNs*nD6g^4{imQ@-Zvz@5pjb<%DVk_yqoVN^w&!#6A zf$tb@EQdd?5pusf(yGPDOgVf1>*v?4-jc8-K!xGi#P2NOFP;yn>KI8MMjtgWp|2FO zmhwO)zH4Vy_~O0+(+4Qd*g8Z_`NF4kVP_)yQ8z%nEB9DSR;}9M<@ZS;z#f7enT*W_ z=`kQRlj{51RaZ z*fnEZ?Ht|6!}$tkh$mij^IxT+=ZO+!W=B5euT1A9FN!az3@N%6H&E;kd}kl}o^{{n zQoFf2gvIE(FLVbCy~{{%UJSXRn9AUOdc*)yUO{88U4K7Ye+U$V&&EEeBfas&3wFQX z`cd#{5Ystgi;X`wDooGs#2RHKvZIC*>M-IEFI_@indLM!bg^$A999*TCtx@39B0?R zSR0wO1*kABMap=sWB$l8?C88;^_**rSUvBp0K+|O*_GboBSR@<{S;xMDvy`qsmlxq zwDILPCPhF5V!EET=l3!)uCC8nsgoL4bW(p25>wG=@+Snmgc^wCMGJ z#hzvDz1TM7mgq0LAxO{eRV_D%R$Y9~5dAQ}gPzxC&s);eo7M`K)LiQ|!+y^K;PX z2F7GeHW5*-DepM9wXw0+{{~Fp$7SUEyIta;Q}eXWf0z=qj-5>7r_>-5MuJQ@|69t5 zosL_XtDY=n^tNXey{1~e{Vjw684fkj?dz!&UsO- zP0z;{YWyjI1(`TXv>;;^_Z?dgQdCET1@eoAjod|_G!fPJF{c2G{(QjwoU?lGk8NFj z1mLk!Yy9{3_md9_yEowKFui(N?UnCHm}ub{y?aA@m7%v(bJ9~&zaeR2SPhO zHd5_;g~+Dh{yA1&k$o{RhTluC-ILvXb)Gb|lq#p(jo(jxSYs|$uUmi#M*V2_8cDji zpJK$3Mfd-M2J1=}*5!QxAuGBuzclX;b}$F$3kf!nnUqKn)c_NR*-904#Pyu~ywyIy zUli6C)Bm7iJKEt7*f~^2RfVA9KVb6!`BDg&KL%5{Jh7mAcSbqb6cYgd-&X$hU zqd#g1#AR)Qcb@%&@B7CG{>}9Yd%U&c;ffeEHKd*Yw5n?SX$1o;4I*yI8Tn7+{5_oE z7k}f(1=Qi4%tdqR^k>PhTtgx|lW_YTde|EpG62O0sR!K{K#CG5b-jvi@^&-VW#FOT z`($ibs?6aM-WNSMa{dIA@!fZ>5hh{^)+_CHRB0h;8z;t6=FH)0kF~NCUEUrS2uf=n zaw6=Yd2O^Ct8N38UoK%c{?Y=x$Hk56>rhn?$YSCnl`5huu^lIRYOd?LBymZi_g{wX z(xoQT-zfM|_@2_#P?$Mpa0J1^8vWCrlSIyYQIa;TS7YoT`G+@!1fPyHvp2BOt<$+{F^Gq9 z#2~uw2!zd!`46A2&0Jh=fSec8N`cy@Ofd^#MkCaJzu*g_u{UUA)Y2cmT-id<#c_;( z)3Jko<3!H6M>Y`63w(a?S@AE3_B?(lT1Rc>-Tm?$0>sdSuejg^$%)!bMC$?cR4A{_ znr*E!fM^Fn#8+{y9ZtEHbs5nF1;p~d*8J}e=JL<+(f$?66GYd%oEkcX%ylIKP&WLU zGL2pJTMYdR2AlnP2w>AG{-&F)F|W(XA`}Fp@E;GSarsk)Fv0&kB(?$Ns@9nlRu=w9 z*Iy_W0y^1XlmY{OnMNuS7?n{lv}k+n0rvfrdZ&>Qv3T$%O|;=gok5|tm1y;(xOiv+ zsE#Uh)2x$jT}^p``FetY6Nl)4fSm8+WrUlG|Dide?}99W3QD|jxur+OF@Jr8FB(yo z0RtJaocxL`WkB@P#Y2m)tpZvCp`|40oVCf2>zXg%$_AhSp{X@meOEaecx73KJJ+js zb$POYSqVGXGfkizzWDW6WUv& zW_gw>@0+r;y(2dm(ouO)03Kj+J^I)NAR?jQgp;LzZ4o_Z*|SZA%m>MHN+^8 zqWzgv!~mkE%6*11lxL;dcyMBGUK2E4^-FAT881)XYKjkH;GK4}v=(CE#eQdSur>Qu zmasxPGoPI$XcUK<<2A`~ziB80i4eekC_>B~MW;ra1=A3^^%ZNJIAeSiIYn!a`mOMAy zIF&#97_lgSK&!op}6?WP%Us>}&V?<^ML^AqMhl$p@h?QAE#&O0AT2bP!fI360YF*y6JTej_k>9oU=CC+H++N&MX()*jI zbFXgIIdY?W9tbk^l2cMf+Vtn=Gzt>0jqc-3ubLKmpKOo{G2r#vP=>DY?pC}|u`Rod)@7;?)U&ZuCy%Yw4d*;PP9z}LDwPS zQ{(FVLD~)ABVdt5_cj^}z3pa-9!j}x5&>C{MAFNPHfuscXUW-S_Rvra$cS;Fckt}d z8g&x={iPb`C&AukKsB9aAn;ba%SAD9IR(YTqZ0}x`eKl5^RrN0z zfHd+OH?nQ)aDlr4GZTx9gkk2nzt=$n_o^dDP-@?+;m zw?AJM7jXpIzOMgtE^c|_%DK)eJMs>vHc zGcng@Aw1af2P`Igi?3_sMl5shY`MB1g3@q5Z=ey%3u3cT`$)K+krr0f_q)8?{DXCO$e7&^ z!@eq%fs(h!+|a~bZGMJAtkAqgSoQ2tq(<_iD(te^?)JK!(vnSBijn9r^2L$myo$U!pFUN zP#ydH9E%@i^}byM)C~in@@;4)`H? z4ZB{3u*?;IX<7IStkf(~SC`cT`dV{nb4 zI8X)Hzp!AVvQ9IJM|LmpM0czEXx#cKUHUi#_PCQc;umec9dV>}YKc7gf3X-J|MnZP zeR7D1$kS>Ya{t)}UtNw-;~@7OdQ>YGjORej5As4IyHx<&#*YAN=TMt)wDfdyVuCQh zy0}=Vkj4VmZ;(#%ri&jw2y$(`Z)OocGxeU(4I|5yP(2!8K-t}uScGESz#%pvUFrul zH&~oy3gW1K;`8&|;_orvq<__!AGx{HvI!E}^}qe`Z|OkS36qG%1^EI;c3*WjhBQ0h zSKa7a1puOY{{pTmVqil@&@T)#vqbTmc2;ks5xA5K>gaz3R%LHu%kD3LdHm>>&W0B= zUqNd!NWCaZK!b&-wRfYPLe?H5+(&3;XwfQ7J%n>6XSL}2#1#;ZBe*Uc%tb8;01)dr zZ5`#;bCF4In4#XtyXnJ85zfmx5 zTOt9dDew}DN5^+3WHI&~1&)EiO>v3yN<#^e;>j15_)R8{H9NYBvj8SIl)ux$R;K2W z*Ah}~1l#ONydvG;Bp`}1I{sWj_c352_R*zmV4g|vu4KJjR_kW+C ztWl&g*74dcMeJ%@rWW%1o*Zv|9>&8%Mm!yCoM1>%(or``na75FzHvZn_uSRBEXx7b z1uzoWJo=uhN~xueLU!3zc@dZ#f`+=w3k7&sI+X`+d#?l?ED8=a-mILd?NE_Y-TY?L zvASYoamrkJ%R};;Hz@~Uc36mg-JP}Ot6JL0#?EdLV?$H-_p0=(IX^?|NE9x=Ex$2h ziMez9OO*!WM)L(jUlEw>Y7z4MovU*CiuOP}HUYqPBVcx=;`1kpPx{j`SSN9CHM1K9 zVZC9l>I72&igfqNwQo*KH~^R)@2*f&TEx#yMsGXQ3p-9`-S~AM#mFW@M?$^pk)b&L z&=8r??hnG$NezO6Vzk_Uax934v9r^-9n|{+I~lF+x zno6T|H3$?##l1Dv8RGN7)beK9)3Bcm$Ljm_tS-R#7i6(Nzm6RJQ2lNtYF(BXXWt@=v8N8&rke{Wj>)_=q7Apm?%n69Vt7s!hC&8A_m-L&U8-;=X~9 zLiez6jdVVQ0QH=uC9WAYtlJaXV=qx}M%oyoKiv@ikUv!h0jPGpBdxdtVu%?RpbT|9N zRVoDOf*24f^=mR&QmEAox{ewPAX6UcVA+F~uBx}&3&ZHmB3Wg7{KR5ab1=$4E$n|V zP&=8b>eN060*c`2;U=o)XPE(39phiRYk3IFL)UfukU91%3suce3omnQPR z5u{hD!~a=e`0u^wD-qp)-uwSi|0s%&^1mV?L!Uk9JSS;IUwBg23`?G9NIECFn$8`)_Ly+HNJI!MbgYQ9Mbfy_~$yiQAt zV$m!cCPfvic<))jpZO}rMq(oDZU<^@39^HW4uS?;@WK(U{c1i{zK6>N<$}IR%SL9$ zz1Abb0B4Mx1>~Zk?$=_-28AeYXac6Xt_?pB%NWADCRd=X57`47{g(1x(ke&d4v`j#dEB0~52os$ba3X~wuKnnqZd zDm^`Yz?^Hm(m1359Ic=z7_kft3Nq|VWWAD^;S?4Y0u>|Bb|CJQfS?wrF{l3S?CM%9h0V0_&U&GUK_Cn$hj@ z{7}jQD5gM@7M#ENRbkpkRE*UP7Kufex2g(R^w4|}h*j5iZ6`k51llB^w-D`57eYod z?u_&Y9|0eCINCM@M)f&AdA7bdH}@R;{`xik`t(O)(3z*F|6Fv)%)){h0L_#L!2uJH z2*zT!JRMMM0<)m%Pc0Uhpz2G*6oWjh)5MKnq{o&G;*$jd8EB@BlvOvU@?zQ{) zoro{hR^x9RR53s4oVcYsvEHW#G<0;R=r|M=mPV#>!SOqvK8b>7i(DK@ym?xvCJwlj zMt1_B+z*6eVBETem*4;tdS^g*JYqB3L6BAr}XttgenP-V`BoL#R-hNf)9efJ4DiqhZ=fnQ!w5hM z68C4*Yj_UKTIY5AL49lFYbtNnpgEW*P!%els1_>2N5q(!&d==Uw>_Bz98g2_-Ei~) zn4e_2@e7x#!cAq!Pc8HnDhTYW^wd4-r(5rM6QRRIghz66I9zLLdfL=X^qrwF(r+}$ z;*s!O^$XcP@EnKp6BjUq8f}{oScr2UHWMG{K`XD;242FJ^C1mpJN;qO4@p!}5m@k3 zz|9y@2{JgL6`tb@w==Bg4I&D;L|k} zeP=3+wAkY|+w2YAIar@S5j#31DgkO~2?(=o>tux?3h*!ujl>=fkffN)U#!oz%&u~H z9ByI*3ADv`1|Z|^u|#paICEzrybXT9ia!-OS9{|Mtj>Q|%{_HwAWH(=n#yyXz436y z^My#`$$sAZ_rM(*cY9eHbiR@CSetKd;8_e8C__-iT-V0V4`=N@Y{=+Og?}KBd01ff zdBgk!X8bGwq_aPoQbhaV12+^T0pP$QXWJ+~NAu_tq!6yyO!`zX5|1W$qY7y_DgW*|nfm#?z7IZY1o zB0^;dG~m$`kBq8gvVhnz{B-jpF35gITfUdYI((Nio?Bs)GqfBUuCLWci(~TQU;mY) zWj7>i(dYyPDFUF+ftPoG{aQL?ujV7~3l@dUOxQ1FM^?Mg<#1CE5qV)@VfIc=5)KZP zYzmv_#^VRv-*2xA-?I>92FEwSjyJ4mVkOFBGZ_ftKQJ&bv{{VTOmwtN4-?qL8Kl+D zx44-Hmyj&(-JKnOklnMhvk{A_oHBLVS+Q|&f!|Q0`Ph=<&!!_?izXQ@YbsnY$>@nK ztA_wopp63`@Eh<_LbR;y?Nd0Ouz((vGYcA^WRTx~8ZCcGx;-ai z8qRbXcEO;4EDmhJ;cDP)YC7mH0rD7Md>s_1fu^g3DG(LTT93V{Rzr*#~@(Lu7|ETJw1VcAOA2H{nr=`S14Xb-1?_- z1FVT;8K048gKJD1D1D^Ln?SP!w0jU;o((u}C)_RuKd*Y_#Nv2&GO9t~b?D`1z2HGTG;>D(f5(8|k>xnLFXIGVD*%?Zx_>N<4z zc^!{%fzHGnsD2QfxDyPiRNG>EN%K2`qJOneDXU<^G|9+XN+ZlfW%Z%4c+7F&gDHtm z3l}k>=)9kHD@r;npZB0^iRHIFC_6en2R$Dt(r3%a)HtMr@%M2A$&H9se?9zZS91Az z&%~?NFru&DeS!a~F?#J--FIZ{T6=peVgdr9K~=x=Bfcpv6)yG$WKqJ4{qpc)I?!V& z_{eV*K2Ze^XQ%*GcV<<1H|WFXwEDzKezUDVIfHTOA8=O30Ms9@tEZUI)F7m z;SDH2_DQ=R-HA+0%b`}L^(2oz;LBh;>K9Hm;_#lmh+7`2DI8oA7QPLdUo~2;l9GO# zsrz8iVmZp;4DS+Vw`UvDaKT}TdEy<|_Q8*lUq4H|s+LGLi9vU=%8WWdL&xE>j2wzl z8Fm14F5_>p(Y{ckQ!z6xaHF!4MjG~9o%?YkXa?T8 zE~;~?lanE!^Z0CUvgU&>U<>?g;eEyFo3*8I*V~cJkH%W)3xM>w#=YMlISRP*GMpC; zP&7o0t0!>KDynm5K~OceQ(xlui0;cl)*E=mygb(KuGPJjSfTqblQiyESz@LDcud6Qob;e@_p4 z?2YW~z6SfCZ6o#M2}bh?f8PRX{p$$gU$nydtPN?>8RacK;BQ^ES6s}usC;Q$~rtbtq$n`#Q2bGuKNE47* z0MmZ`)Db3>5&d34eZc&U)`#5Uk)^YPQ(bUY1d-Q38)lgU<%64p=Fzcxwr08cv|SlU zk_|x<{{H(r9RxyBr`RMAbZY9Lf~khR8NS7)1zP8SETPOmo`RLN!81A!n_oqDmM3eu zdlf0f;2L3WHtq4*dt*F;VJ{z-I>X;H6iIj-4&)`R&qTA?49)#odlhV$#groSeW_~$ zByxz&md`G`^AiQJu3?jP@axAumrhz|6{pyR)boZ<%j{t|Ny@2<0+m2SaCJfv80Jwx zeEGWMllaa$w`vko#^||TnUDbvHX=rYi7)ChN`x!|ayT-0)!Dw_e3}m0-JUMRg z7oakO>#`$JbuYvY$41Y~D%`uY6eh&_7` zq;`4bmkSs#1)FtUT{;MHqey8uhzWvo@46ZVGxh7Fj^>zFX(ds^I0ty4ry1_Idv+We zHhu*Dd=aIsKH!Hqf~qTH6SI|T`PX&=VjXd)t3MS}#DF@tR`wIGT?>tz=Z~zy(1>tx zfVhtek8Ji<$EoB*MD~^ka72h(tkfUb+4+z?{<=7)+9Ev6d}oyK=Y)D!xr~S?u%4Uy zG;@cH%=}mmb!hZb`!5(&nE$q>Fn=IXCHv9#F)KsN@i5EBSO`HB3pM3X6A|g)fXPL1 z7w+1{<;5o-XN&J-7tPIxmG^PmQW$h7Ve6Js5=9EQaihZJ7=K;2=7wd$?c2Dxf*+mZ zzyY__k9Qtb4KnE(SI@d`ZAt-8**@I=yy)&Eg+^z*Eh@-=S02{%ypziWi^}}a?E44- zr}!#3eGB9<*ANLDm0A&M$l7}as{mICj^`&a*{r;K#`pW1{+qOdIyw90pkMZdKcJvo zjOb|((NCArXCh7yTO@cP{MAorJTZWtlH{Z__^`V)ip<1aYRj zGvSLE>Q_$GRG$*FgOuFB7canyn#q%dk$&NlZSj`a*?sAwh%tz6j4|}~jKGpz+R#jZ zZ`n%CH;1g`qDe03f$iuT{)Uv2cZLjqzc)kr8&DqB}t>g(a8)^QMRo^555nt zAsp^#nTcZB;_O+3?G(l(Y|VYp3XGbXoLKn2rOuL#4!FdmvK$Nkd?lH6skCSd(N~6RPGW!I@-4qI-=fB~*aA*{-*rY8*XE zn^o?6KRFqGN$T*VilUsjAOkLS_y$V^!tw4aX0wjO3Hf0CG>IHJx2waL)_t3BKaCSJ z2Yrw8EstS0G)HQ=|Gqxd77$uET2LU! z+K6`Icp-so5#ej1(0U|>B585rE_1LIftxWK5Fry$>7~!}I;HEGdh}2~?v~IYmXFt^ z0wVrCKd!FlS8V=3Fc?J+4tXNtOp@0&P_=xW!~IEc`T5aBD#%Ru)2q4tE1Ir%`IsP2 zMNV+viMSzo@|L}lF6|s$l6Q8O-dnq?jlu!wohlc8@C^z>t|xF~v;&#Zf_X%G2L>d_ zs|k&2g{Sha*3Ql}4sPaqkAQChX=z3Wn2yn7qig6b2F^i^rLWnO_t<^OT=#AU1*J)r zdn*YNg@utN9VT&tT#bAsf)4|@>~0ORyW5G{(aEsP;em~m6$%z(9f*UN%?)_lSbp666X36M6pYzX}aVxJ4{4yZ-Mn>)EApU||~u!IlfMeXFgR3MG@pZ-X4j$ufj;C^7M z8=~RhU_Q4I)j2<}r5DS>ya<*o+i8vKXt+Eky71o7=5*7GHRnX+%Dk;Tn7oJ^X4;Sn zgbY8@m(4@+P2wZxhg9jp>ag?ey~1RU1+l+;7{Ih-p^v4Xb;=MmiT7!zXyKmp*34j? z^RZofw#eJsH(Lvz>zBVXq5%|%D0Bwqv8FE))P6=qs%7QUeJ`MN-TFFq?u%%j%n03< z%Ogf~;L2OR;ni6D|Aq3ikOv*{_z{)pW5jM)h#kr~xm@BHy0<=Ehr_yf(Y!GD;o1F} znX_s=Q?P7~N3v*S+_xpQl0^U%sx6tIY&Tv(ROjqYRck9RBQDP5=p-<= zo8a!|a|&f5w0*f06^+wb`|G>1etLw<+DWJ_B>^fj5SGpNj!@*BOGjIW0c2aJkr)wo zS7*r2{Fp@l?@Yi2?U?A0;{NP!b{U4!e?hP_sQfZXl^8n|^dUj5wJo-zMM(5gJYP{E zQ>o7IcOr7b9>^*{hKCDETtqK2N7}!E>%6Br&kOWDK`*w;dNc;4ccE)OXqt|pW6yMvY8w zN0#cRwx)ZjOtngy*r7CA%>rce^S)1)fl4eaWfG|Qe~T0AdiK2VT>0TuLg ziJD%P-Y3>cr-T+2@77i#(0`fb@|dYsbCyE|&lW(|%1Q%xz#E~ow4VwnoKD@{>gIoz z-Zp-@_<{;MG!YGGRF&S)fmTBd_*xK%OhE)19a8}^SV4lOmq0=FM$T@!79e|tOzgpo zCpx{vRArLx^()0>!34Vq@Sg?1fAsKhaCQT8x!;+O1)KP=&x^j|UO%l=UQnxl_%KxA zd8kAo!alg__;2E>G&D(h6rf*alEViZY8MgsrXU9w5rR|U)XR(}&Y188x$(n7nk>AV zHq{ektwEoIyuUL^<4LhEQYD3k7dUXSXO~DYq#8{bW&4m29NL72u`_hmWiA9LOFUtC z!4MYqP4ea;_SD8P{p{)KtC-tTu3}Y$de~c@-<)zAY^RGF^hR8$i)~?f0x$NzcE=$n zbFbYa^b*2@+2+r!PY`df77NiEGcRfJi^)czW1JMt$AH@&TGv@r~gh-?}1B*4)!-c{A1d}l+?NT*WE@-me=@@6f&E? zev(Df>%GlOP281iUlm`LllZVqvQP`wDgi(mRBvib1^4}*I;-M=NQS1eXr%3rTSj0jqX&YD*cg9`gF?@ z;HQsv{qgSGv}Y(>U#kSJ_GJzZI(YdweW?=ggj6Lp7gwu6X@Ya1Z$iRZ9_IofP1(=} z(^R7pPTgK};(+ZX+xCV0cem;K7o9TIBfZtj`vtOkwOaZGFayEL2uKJphaJOeKVuK6Zy7v_%8$dVlfT>JGhukk`8i=9IbI*lDl)tGfSe4oY` zd(}=Rm{2kfwI=GVMBcUeXj`HeI;YVl<*S}mTY595DloDEF_1Qkv(I4;G7fh4qr4rd zxuEdk;HS{K02P+R;*u%;h4}gy9_^Z2mngRkd!>CR>MsV)J8$l0jgJs9w+KH7VEf!p zJKI%H7S7!EI-QM+3x`wf_N+}yNZ4)c0bUX$wnD;o1CwmWjds#EEnMt4ad8wDHs8Or zWT3rerld-lSt``-_$VcrFoo$K+Mc08W{@wHZDV+1WSgF)7VBNau3?bBJ=@!c-fMoX zsbJvE&Aj%?O6JR#@2qZESB+ggm%Ak$Mf|eZ+F2njZ{%hPPk}q&YbZ;WK5il8$y7PPdJ`N%bcGac$*PzY~0R zy2Zb$+c=L!OZW_$*LSjCb9xfBCJN@j!_UTa#}sV%8-rJ9$d4~QbFWj$C`}v3!N`&R zevOL9=`+akhj%ygUV;22sgo1TU!)eLJmm6CT} z0Rh)*YWKc&bu}yos2FK(lH|^4GeTpeNvcVjDj9_@t1G4rKi#rTYkyz`?Nyk8jaTz zvb*QRNN8wMAg4sBsutPwHj=x~0dK!#%M+hsSUIX^A%C1a#JE?#Dk*e7^#Y!ClZf!= zy6-9YKXP^1D{j0qu;XzmCroyaISX&YcXBI3Ia;e+{ia=Gnvn)BE~A9RjY{h$KGMC| z_ld%qohX}bYYJjqxuUGSKFN~T%h)UJeP3L!Fs#^gs3f0|2vh&d({D90$!Wb{b$jZ+ z*9{G7BDY3G1#wylw{gb^5_Qju_oy{sR^uL=uP_(JJvMV4|4-0W&-P(jS40MTMtA>& zm>Y7YYJ|6J0&;VvzKbBsN12u9umV`jTr6M$t!aB&96DTC9+avcug$?Ye>4%9cB4S$ z-|#NrhAsB=0~Zbjsd>+VqR1XARWxVuI3R&}Psz>UxF4SlG0cXf1rC2uaPKtw^^;LuG{$-eiA_aJ|XWnc4aI_*`+i z8_;DG106juaroc3EPbmo`}(<#@`36OSx>;2xNNaw_l*S!{+|C0n7I*d)+8PGJzJrt zy9b%H+imfJAp|9;`->9LlIWYD3{8t+`lW;`^taGmVV zaIWs-vMqUy(>G*-iJYm%6W42;V|MiPM}Pmf0wiz>f204rBtSOP7ByAkHdr8a$98(U z?kanc=3(Z`%P=J@?CjFRZP)UmegW`LM9;y2(b0OfWj8;mJZJ8$c$JAM=ts$x(jb#` zEf%xz6{glg^`54GkPa8k>(eDnjFf=jLLUE5p9%;^f=YC{K2oo)8=Z4@RaLTIscRuBzQ= zi50l!409PO+&hfcoBDR1Z{p&!vK)}f5?40K9`l^TH4j8_)Ml)ZlBjO8`bDXHkp;g? zXkQ9}XXtdjB}fK|8dkKP9BwToNggEJ z$KnzeDInC1&)s+D*gzH(;#uvQOF7wDxkHQ{VQ|U|0^O4iJ0>b$x@!QDWn~?1H;Viq zLusyB!TmUwv9HgveeS(LvawH8l;)4^28^QOd=Eg_mPp^8PbR}vmKf)^JisxVnK5R& z?41$kF|Q=|zh|mU%BZ0D+RJRHpFhp?IXsADKjY0bHd<-y{9ViVJBJK(zmV*dvfe{- zTxCJwJHB(Np<#$c%d2vCKT%wBH}&b2Wj?X`P?{qaTO%)a^Z&rocj+onYoZ(`QOIf$ zl$SRTAieZd{^+AY|9AN-0v|yXRTXJohd^rvl^)88i_d8WZUK;j#p=Knj{z(H{(ZT~ z@-_+CJ)CDE+K&@*d3jqED>YdFkaJ9w+<^{V+j{U0?|Ee5qky+$Je+tWB(i{@FFO)Y zXqN0YXT3z$hza?c&i&#dm>9A*1Ml-UPRxF)f`B6wry@D|BD{icRmckfveBvTM^Q7& z!^jZueCts=UU5IR#>DpNEWMbS+0v3aLV$xq3d=6zb3k%(m_zTxexT0mmU2VE>!D}G zfVfb^8c&MI9WXWPTOTZqWm1u;of;fehR~^#^6+3SIVIE)><~H_X!PknC%hj#f^YDx zY`Y;UAR%HD3b%-FbtE=~T@cU7bAA++49G{}dP~l}4|2~*RZp)keG9Kdd8l;sadpeK zifu&tZJAvu`=zvA?APD-Mm5jEQ*&`$S}}S(GX3&`tYv8nqlyjdIWUi({|2bf!en`@ zn)>?tM~9CdA(c8aOWj&I_Ot<)CHB_TZ`Ii0Pm783H!2ooGw*M0yej+HYg~(Cb)TF2 z^L2VwSGIU9^s+_wJs^Np)k4NFw(ovN(ntCcVrr$lSWHvPMNeo^J{AlN=HlaLu-Y1- z7MPF(Wnt33R|^KxF&8UYUwC-fbf=;0Yy35KE-vLmc3ILtW1u8fOoWY=hX$U4HygJB zT)E5_1Br{SYzIpQSU5F~=gv`|dPay$yN%Dr3!wuh!S7%f4~cnM2iA)haeCe6FJq-; zy#4*J%4bmc!Mq8HB!j*&j8;}YFLdi_(8cRKUQ>3(zxDun%pMvGWivfcx}3>dAc@5b znCmJXZ5_H3Kt{!1F)ox7l51kO&=`DY+1`MRqp9)tP+LiE+C5#l6y)v|hb9 zg)BRWNfq<#LUe`!zuw(r>>RIu(fJ!MKV1s~{PX@L`*NFgA`e3-9MS8QT)1BQc((sB z`~)E-p2&|}aVW(-+&v;|3ln%p^*Mfb*WSpF>>u){$~<|ZVsfD4^#|6dU>xN8t0KeE6p;r#GVkPYG*Qnr7*9{eaa;qw0&n_AG-LFwb7oDKc#b`-7051 z&J${YEL!TCm)|O*h6G2_;ats5pp-Ui5`gcv%V-w)t{Hngr@ayopNCc+=S z!bJU!Oc|HfOQLa6@ky<(*;QiE@2KeWjCJoJbL}d@`#xVs&fPdV5ipnu8n=OmRgz7+ zd7UZ{3+A>h7EGi^rVpYErzQOQ(S-3(n`OkGhx<~Hs;Hz#9Qd%WxD1=sh}e1-I9hMc z9TPB3F5QQ>F5}+Zt=|qtn8TAy9t5vBD5}|8D+Uu|npSGz!4w|m1NU|v`m}7e*XpJc zIe^>H5m85TevLDH zTr_W^7#cyFBAU6>wm9@Gvr52VY}W6-Vcs?5P!=0Jb!W>|_M>@!cY&|%0xv1(2tw-6 zV9A$2E(;^&p4ChS#|5qP$hXW^`3cAyYvsNI_I&1SJBqz%3t z1U3&KVLyZQNH4yKijrBzm7-;F>D9WsJ>{^&99jVRrhea6Mrt@$9;|}Z@d&eJ)(w>B6La?&2iZjRQcR6YSH#~ z9oujm=C-ageXH+h(@!|*l7+vD2K%u*a5cMDf{GpoFUoWEYPMU2VKL&OIy5DR^%rzH zXp_5d10f4Q$2+KFbzI*>I5GFlBB3|3Z(!ol@v+2Cetu~r@Djmc^`g>}cP73I^&r1N zJ>;@aZm>&{?ps&ovU^w%^RlUU-ez|u$n-kX_40|wZZQS-u`CxQVWPE?|3ITN#kwDS z@pN5uT0pARw*4upPSe{lL~Yjf&fs`-Ucbfiq2yh4JAmQrJm(82F>86dZr{;?n*PYW zzwc&#po6-{r}yhDr5;H8zz-*N0kVKCBI3r<#dS!UE&HnTZ@(y zZ9gN=Xi4wRr)-qJ+g8E+!F2d?hcr1cS*@ihPerV$tk(;&3jV19bz3GszP7;xno|J*Uv_u z^0@7;F|!cgvZOL+_*$(Z`SyjM0V42_>akz2!$d|IvV<@iB81(oK^vf;Wu@aJgGCVm zVIvO&>w}mS%h-Fy57ddC5QKP~vA@jq)=#_s0zTfT`dsexiIv5t4zz*yXf-XhN{grp z*RHUfagV+wd-#5kN#NPLdwQ=O=xPYS|7q)eRa{$n>DfM2kP=XQTjT4yXDAO^BUw!R zI;b{x^eb=d=`q?X5Ut^S+bbKgFzjwjxBJAd7M%;8nbmR90#L)dIC z^Kwgd*(3?Z+-5x+bCUD}e#s0%!!X(puofF$Oms94Q-Ga1CY=C^p2?6guVvPQV;JqK z!vfFxq0#gPtkbN6N`6su^M(ZJslDwmMhmm<3o8}dx4_+K)xXqjFD=m;D&x?KkVV;- ztn5KUbd3Z!K zSQ$MONBeuAJk|L-Xq0$+^Ta?@sZT1>hd&|bS$R35py1|@JpBL@{oKRc|4dt(;y$)z zRO_c?RQ^GIEB9`zhJ(0WU+VVb%$PFI320G*Br19y0;H#V>qvK%Ma9K$z#>@PYwcLY zwGa{)Pu0-~qLlH1+zbV8BR^!C;HNvqZHOoof_jHl79p1mHLmD}^;_yL&qh z;N(*KTfgV%vgpJ^UI-Fw&*TSCp}|77!w`VHkta=(>nqpD2_?pJs)H7}k8XYzM~4iX^IC^;r5{ms9zI|W3&wlP zT&i7r3)OznPj7o0fcRPA?MF1WQGKN-;I~KHKhwv5t+vox!O8278@I7-%#7L~?RR$k z+EPoBRmM`g=mehDn*v|c!rHRV*}UvZL^!+4_2YY{FWHX@4Ii%_e$v0hBMpTSI3q=S zC&nb9xANHOwo|=L)o4}6kI(c?leuK~n(IQS%hwK8=yZeqkB>_Ir_2YxRd2N6`uh6b ze1FgMxyKm|lR@_SadOurJXFZ%K2+sLlEso|rh@$ImueyZ$H_5>xOb&878w^eBk?Ld z1~MVzyg$UjPg!)d{>W|f=cDTUk9m0S++4_&jHNw)fk-<`zTK>bOlhbSWNWHgROm&T z349S`ekG$o(MWjL4wPc03E6NLqx1|6GD7-IG0Hd7(>qTCR(VIoQw0 zPl_h+e^lX?DwG>x4rW?xgkw zwH&<`W5RGWh{-PC2r8h%AaQV1021LOIm`^c6PBP1wYTTkji>mhwLco>WsRD$+=rQvmJGO3K8m z2#$tNi>=X3c!WJlQOE8lS!d|!Cq}0~3Uz&yYA<)jXpK60kh^qKUPmz5`gxwUWV+e` zr@E^xrgFh|!GxUqt>_y@Pquw23$ENNAlq~z`q5`Nx-f87g;$j-Wg(g-Wp&-kH!v`; zV?Fr11~s)Nm8zPGK$d+~k!9VNFK*Q59Y3cMqloMEP~zbhygbxsFqdkrdN2`8q^m6RXn>B+viod`H3HOc44 zT5TabI#jmC$6dze=AIaGDHH_die~c;uwuHoF^L2}mFs@y4*9yjs@(~W6%|ao(*q5m zy@*;El;-am&6IE!q#AJ*PeCcgV#TclhN`DW& zSm*EUOA%P_F*(x=+nf`6%Y^FBov>P)Xwz4EMIs2vajTnt_i2+0P#PjLGtR-HS9B44 zmYsMEO7CBexbwa6#?sg7oQ%u0Enlm9@*sx$!MZZ62HA(%;_`}($1yz4q`A53HAAI) zMb2!L=4Gsx5=3{x{Jvysx>`)YKJN5yUghQFB0&MQ83Vph@KiOpQ*ieN8Q!S>eSf|2 z*66E6wg-6XZjKiQ2W>)2N^G&}Dv!7D-42$F6Q*WboX^e8U9^~K#XnrFmbY16l!A@N zx{CQ8QOAy?n@$u_XhK576l{bScuKYYU4Wt)NtcVZ%WOSR@3XP( z&m3p9+@I;R9-q-jtS|prQMy)Nk(Z~PRR4nGA-`Y#n%cHRv-NrABSN+rmS)>?w;dIP z#gQC$XW0*rT%T%ddlnM5-Foj1t_A~V*+Yg`DFAR`uyiM>!6}G*#A9?62afD{H))HG z?mWoE`((-vwyzK>WG-GzW>)XVl96G1fdPjb*c~1pK>5xB8*-ez8hqi+S>k;!5kI@{ zcg0@QtvtFX`sm}~x`2QiyZbSgrPEBSF63RF_{d>>OIMN^%kOo1AGNdIBy*LcII=;y zujWPEx8}uXP+`+Vs6Q$IAAnPg_AC+91fNf}q+@k6!2`6f0m%n&SwcvI=`lhEbhOvH ztJY9MgD72z3uA{e(I2IAsz1SnM|<4!n)XGJfYq$r+v;v^P^dzrkjcz91+}z3E#}HC zC9Co%XYd3~(v3F0Q6j#Zx^BsCwXlmw)AjBImr_6_5RpX<=99|!Y}#04rAx?ZZ}*0K z{z=ULlq>B16b%k4waacQxt;>1G}_sciP5;V@y#lsiG|653ea?r zz7qR(!rDvoz)C^b5NWReE?!Vj%LB}B0KGLG%)MhW?)w)jxC@3SAyNy16JyXvqWiV( zv~}c=Q1Ei9Publu)bg$O$*P%yKc1SK)6H#$$1(#V;7Hs@Vl7;R+X$-Tu%c@-}tUPpqG39o05sa#EJA&#o-W$z)$)nCU-qlxmeJPI_%YB9QX*3*34YaQ;(~iQECbLK{@>J1WX? zfaP0H3oG=uS}m|_33*Yvusm4bRntA-wlSbnxnG0g2qu3L1(NepN938_OIF#yvxApnbK4+$) zPJ&+|z`OEQV0QkC5n5vAr!-M1(hG9uBwTxiUeiHpx2np0a18^q^XkfIk#cWj(gZos z4i{3si`FMH+{5buUsIXdv|iX_zqR$dzbu(W?}V-cM3BLaODW22A`nEDx(-0fCh9nd zS*RrJ$N}SEH#szs4`-0N|K!Ohc5=K?<4p|tFj~9Zg{+&fo!*qj(QAId+&Yzpikg~S z)@Ci(9Fxnzf(FgY50PCYP4YnKvle>vEuoIUT!!}oG$Q`?XSz`#Q`ZSY4Ef(LAPfO+ zo)*SQPgT=@JuEnBnAL&LjI?M+?GVC)%V#*DWhiq{Gq1Sj3=XU$5}y}@ zBWlXWkvlWhjLS}rM=VZ`Sub-{R2kqo$|0{GAGlYiWl2{7Z0t@oCu3kO2qRHnfyA$; z2t8Hgz4OmN`0!(3AYP^Lp8QeMoCH6TVU$bRl9KTKe6Wv7!iaDRJprDU)MCW;EdaG@ z)tQG4LpYCh-|UhO1M7}F(Y?@<#3~g39cpLZBklFQG7T{?b4B%@mZ$vnrAM z(Jz4)^T+ZgRX*+jSTzJGQ3U$1cY}}>y)(2UT^u|AlzDM;CogJ=DXpGLxAoK+gSB#G(V56ch ztW^iQKdXAUf7NqrEQ6*=7~~aDW~E&ywdF#4_H20^cmK%Aj1wvv!)j~TW`hsv8q8_q zv_**+)Gr&a-II-1LqQ(2TvWUQr$q|$dz97|W@SPdRE|?E@?PE@0=&+Lai;+?oP$OX z!ucUrTI2sValqz0(EVV~Zs@1HY`!)6Dahd zkIpL(ntT(02hflm+zqSH^bp>RE&ZI>oy&rUEQx3sJf;lt_uoMM_3tqYZSDB(G5 zv=9%Ly?G8QFKrjWY#mC`0E{nNWDSaiZY+mKLm}_`$=S?k$(F2b8*g-E>IQ4Pk)q_` zjO(n=PVM(WsLce&upyMO4+Zs(-vMqXbm!rF5KSt}ykt0U{ z)MyO8BR@h!LV=v#APTq^!E`WisLKy)-mD3#{rqaBle$GZ`7WHTSu!5iuY2th>d*JCv{CU{uM@la4HLC7S;3f5l0L z(Pf4ImT@&X8OjtFq_L~@ZOiQ_rkf+?-XmHn$BkSIsNQsbX^0Gm{4>?5FVFY*BSg@# zOK$J!IlHqQZilflEO~nYKfTQ^;CXDUf5%lttA(2r5NZ5XpA4AZ+bhT~t)a#MQbT&6`#v!)RqB zC|q-)XpNXSGS-c9`L5j@gp5!byYr+G)eDih>cKsAd01k?!p1NR&lpvFCLn*HXO9UX zuJj4_d4!tnSXcRtXM0y?iNCwTWiO_}id%^E#DB2mBq&IqlINYfP` ze!j4j+I3f`qxPC*3FLuVTZ3%Zhv&DHb1rIJlK8AI*CbbP`cThOaQ7L=!KLyk;7pE# z<2@e(RkGGaE?FAg=nbFNHz>t~eA{A6r+M&CXu@gv^{)<5RYA>>AcFR8i2m}QRpEiT zga1M*@7^#@1%8Tytn<;{hk&&zw_!VYAQFj>DXsP41j zD=@qI-}-CmZA&(q0c*(6Kvnyjv_gUFoBRk;9WOZ6+qT*|3@;uvP?_Iv3rh0AqoaJw z4yb5zuk0Qd}e5=h0g(@Hk$q3aLG6L+UV*lRG7F*F3w`zfO$D)4rUhw zUwa@6ru+P*e)4;1WDFY!gh3{K`oBYc&Z5VqL2;N`nDs9ci3@qwr$QwdPmsX#uP3>4 z$00rSzg+at8?GW{`pA1I<%;v};^QrR!PlNMw1x(&igM_LM0h@iejJ}I%4=oJ5K`|KF6587j^jfvHc& z|CgCwPyGK^D<;6x{?|AOE9nIUi&~945D-mj3p75s78o4f%v>&ciwG)uKgtYZ9PDcd zm}`PVztwtadcNXh6?mlA+h~Gnw+iL4UbNF2{>O0X7Q1YoaTzG6f?{4mUN9RIB@b1E zyG6ATqD<9t(HDPo#vf|Fk?Av$rj%cxdd((tnNY)crmtUTPep?q|K!+yS$cE+!VAKn zAirhO7blAcf>9L8V{*T=0CX70!>wZXZ_?3yhRREcqjgB01Fxzrr-6xSj@~la-5aIO z-K7|F-|=yzBEQNdSBEy=5$D->96CA`$ELj8JWM_d6Bur;`1+ndvKI1x&aLZh`+G`w zs8t5sVkDL_J6}G$QzvfE^EDyQksX5Xs+i3GDD{3+aClgvm&dL>b`3gIUFUr5U)ULj z&o@hAsov)hisk6McD$(2(NIq&J~1Kdz(SOkr|w(VQy`*SUS5`M)oNS>RvMO&5rNkb z=b1Q*E8IanF(p#7B?QBv+Bg!7Z+Uc`iuQE5am)V{VIZ{Un11VsIuu>3gP=Trka-^l zG$REKyvq`TUW5dW6xgA0m zoi9)(mzeHNG;z!%=emwic7Y8Kt?Ta*5WOB=i}N4`|Mkmj{91~-^v|&5`x;JZ5>jRdB~1CUV>Zi@7ttavCJRut7qP$F$d@T+ zNp|C{K z@Ou@nDP+NNMJLIG#vD)|oN8rY1BOH2Ih`CDg}d?eH4a~IMj_?sEY zrbkG^L~#(6XZA9~jJ~a+V)ltr89qU%5=*-ly!ka1Ain*_qX={Sx7yg3Pw6SG+yAGf zv8$!6B^U7im%`W?v>Mb?jQb_+`Pb%_UEW@zKkGM~1D;w=5r@=Y*LCgF6@5m7$PtA- zQ*Q26JCW4HASgdW)w;fe6$t{PN1Og8U$h z6M)!W^M$?n&uuTLQ=TE50ACog$iJjTAYC7+EX0Z2m;S5=HAOha`sMUZ|Y-FPk;FhM^Z984)+`EOlB_yv#C|nM90d#s$PAo zZ$FIa)OZC8P55ubNoiuGa->&ss4r#9wTXl0peoholfaaaRaQ7Gl<)5Bz$U zkfA{he+IAFenIdYwJ`OMXIFc$(UkwbmF&Jf|N5ic>|0js?&w~4-Uve*fsE$o<`Tqt zA3f(0q8iX#{Dg(?;fSGlHbYIFSn$i%prqCsS|xFUQ(}G-u0eNx`s?k#&0j=+rKC~Y zf8GN=2XZok|5Z)~^ab%^)etlMrYNf^8FLg%C&B#uAHT#%VfiDJgC8MVS*hS4&mou3Z_pLZFai_-( zTAgeRMsPMl^%cvc7GOcJ67dSjfIa^)a>C?Q#s6!m)~}n|jr!dcdM5wRlq3jg62<*e zXiDY+D1nest#_b=?xC}#i|A9D4{}=)BSF{iBF+2;r;5I9q(fI%3y_tp<=e$%NWHDx z>c(GNai3Wh1}h`J&~1Q{ahy6IcNQHi3B)(*H{mDLN-GyD*~d|2YDLz=_t*C+K7%-siwC&k&~(nkiOa4UjN$2ku?T}aJb1s0@AQ+TLEO;c=vq+ z!pZP^aX}w}(BS|eg8(xRv1PUfU07caeq0BtjU-F;J6FY6mfkYLD zPdz8|CHD?;#U=LA6E-T%v!`-^=&_s zReBt~q_N}vENFN*R5ZGAV~I{>bTE}>*mX%4A-4;<)bzpLwif7M&8>v9o8bXSB%}kP zcG0#pRQ=N}Ip#|TyS67p4h8P%eOsWCJNr16>;(uiS@cE$P2d5(h+_QBvSRiDPPaDl z+SB~k6qer4o8Z2j(_Qz)XbK<7(IR=yWhZ>w+4=L@>X!kMlu%B@OpbYlZ(-0+AUmeX zo@ej7$O)ws`BumCOXb{S@UjZ(@3icAW^fh-K@f+4+;TQ`#FQch3jH^|LCkoByT1G{ z-<(c%>LVa4b*jWYC4m?K5p6d#K z_xd|Se&G({-XT8NZBl={Z%hdxVw0@}izyRk>F6Bb_j|OuOZ(fE`S>9kh3+rUcWAGDG-iiAfh;2#{_~RwMIP`_ z)sRN}%>+hQPVZU+RN&{{!4sQV-$L%vv`4%chO-Z|e?vYQTU6n)fjBIDB@)<kBOg zq|{!YF1qm|;`N`TdK%*!E*Fk?VgmPuYIV+xw?g)9*7Jxl(u)6Rs16OPg`-#T1^=EU z$(;85RvJ#{R~&Zg1cQ@V+U_F5#m%-jwaBgtaTTQ-X5A7r45*zH_24Ar0PB_C$`JW)*KZ&M8P>Ob=%|B#&1Y{o7&&z~Fz z!Bq$h{(k$vbndJD@sNLze_P>tFb+qNzbz}!uE*J28X!7@cj=Nh^eq*iUKuFFm6eqp z{|rrq0!Z&lM4e>0A7Q!fZwmMmGLBA7dDr?A-7FH_A``%y*X23g$PQ*5NknNc`(+|SQ1gyA9y$&0I^LGRL6j*kv7Ffj$; z0Pr)TCDlg>xqEwi7`V95OW^9@;Na@>jo!I_}#H z?Jpikh;Gq{a62+SF)^{zRwjxaAB<3mm=w4$D~?x<`m-p8bU;3 z1_(wC=HqP)=)qcwn0pDL*^O6v`;M0iNAQS={a_xEv}|l_RPT(qGdnI-d7}Tw*7Rf_ zN)*4imXiTT!ne$3itN?r-!K!pZ^=@)2rZyrMMS(nLOJ`Tsj2A`#i=qzlZE)qnP5u3 zj~<^8um^x4bqW|iXX08f_NhL$3BDH8CWWk;Us1z0j@QF0p>#P?=9o7bnZM~&?tq?H zR79ManfWWShLu31;et=;e#lyJw5^i2(6O+!+va|Zat2Ox)1d!lSN+*j&-in7K>>|! zcnjty_2J>6#dI@CLheV-0qA-Byt1-#QZ{*=!+D!QRaG_H?Z^p63)j)nadma|izub* zwYMZ}W>L^!16HDLuaW|Ce7BM1;jZN%2xZ!McB1;WU|wj6bYdeG??u`N;OP+W3ad^4 zNXAP{vEXmX77Pa{1Vn0g)2geUkUxfoezptpVH)|ysd+pmKUl)}gcU1H)YU1ze*HQ+ zI_d#!;x%huUJB*RR4;$|?j12ZCnt30Q!jGb3f@tmN7_3WIl(bEH=o^dD%+cm)a$iA z*ftdw6@{(t+7oqw1bCOA(FGzjL#)x47-A;;MBFp&6j_|UC;|uj{j}RgM^?{g3Kmh(UPa`Y@Mrp=4E68#--fqjl!f93uWL=U-(s@wY znPoN!x0BEZ2=!!DC)75o>7IwWPK5)km z&Qcd{+$7>Sa_<5o<_$bPKhR_-ZLQ2X^f*DGd)wlDS9b&jK1oLEl3_aHV`OS4^#LR( z0-sQjUIYiTC9_xGDN^0tN9V3^diCxD&;y3@46mQ5UZW=OEO(ZY<;VQQAK(6Iyg!3= zD5~TqsUvppyz+Usejx9rv0z8TLV1Sd{lLareDlLrAI;T29!(x9NA+3zVA zEB5Dap7{bzwpsL=iP@SS*9V6Q#97a@X6NOBJfRFmW@j+~!)9j8`tuu2LtxkGQ2D3j zLY2!W4Vb>is0s;cR zE;thb&H>PX2{LO_VidkKSbQ5?kat(I4EzfAl1k2hYi+Fq+Cj6SrKJV81jz6Qn2n^N zN|VokK8o*N-9b6SqFslbbDW!-rjS+l{;n}xw!giEb;d6^7(&kI2=A5jzU9VjRbq=M80 zn*aRbOkd)WJ9Bj<;qxcQJC&hU${BgW#8Ag(GfZ%{K>HeQ_zr;r;Z^eS_y2o1e#&k&ywx-pcCc zrk8x$8wQOEUOGCuGw$Dn9hD1A48Lzr_4elJ%Yaz}MLgBZouI!F84wwk8AgJIV*8bt zU1i+?svY(BMos;T8oK{6HgXt#RZl}+TU%LO;Hrd^1fTr@zRss2u9h){B(xKPek)^>0mVa|o zK?{yG(ZdpxmBj~Y2LHg-_9L!z^pVlgj4vPdX^R0nguz!VvY>{uPUsHKzR}QdaA1ea z;Lw2@(N;M6#PoAO%DevM#sY!m@;lvc^ZP1O3uGC8a~`LRAC`0!mpcwHYzqquIO8t5 z_E}SHZ7t$BiW-60#~&Y5!8e^g<$8~bxjpq*`rgSA5arkH-b;@Z_atf$6rZsYRZDP!Pc|AS(H(*jjznn z8vMjnKE4qHlEW>;}Q#2eK8g|fY~8DOkp9LJm}LSdfNv+GVRDL)nE99 zGpD>MMFC0kUEPIWaQSDT{MX|6E%Ey+_ge5z{?9x!+#gX6hvf6$ zsgsbCk>O(B`*L*0CmeeWo}}TjXZ46t?-D#I^YGJqtsva@ecesrOS8N}FIioW8I4wn z;f)XS^6Gi{{-t(GH?7$Qx?7sf<*%r(KM$eu!;TxIDBM}Wb&Hy2vG#cuQ62SaXTecD zIBGzjcP5HsW3Ox`D)=fh3P#d0W0sUeO2RDZx+%zfwKt*qsK25xu4ykzNT z0$IMv$!DVl1w(Qj@XcmsUM{Hfx~9rz^(s{8(@_RXzNh|rrxVN-csKZD{|hE4T7 zx_onqjpZz9w(gO}Lz}hB6};;tCEng+Npj3ZWoE6}Zd9{<30%k4v)T=1CHk}OTw16x zN)-gX*148kd_<0YBbvieN{XvBs+MZev9a-6M{RMz9eJYm;goC=h7&qxsD+V;`6qSH z#4VewHk#!I|s+O#HDHQSBy ziCJwh%v8VOhx4?jTn%zv&$(ZI19QEmawD&@K3gD|AC9LO^F$v@&eOlP^-Xc-ijA9F z``(`Bit)hea3(o?AL6i{qsAWEjDhxwEEyH=AZlvD0W~gbQj~*w#HRy|a_$fA!-tsM z%^&TAnELZHQqarQ9AuPpw zF8--sT@3D!raO;u47Rq_Mk z^kT~6?EOBz_}xxN6DL;kypIpwe%Bt?*0!7w#2IdGZF=J+b9CVkv&LOZF{Lt$!c5hg*J~5k$eoRYH6jjbMJcJSu@j#yr+CqothsYj z#qzMrT`?+aziWCX&h5;rw3*Ep7ekJd^&eU=kzJ%DyVkw`C#> zDdPPRjSs2W+tQxARA1(u_%uqjI*RXBs$760pow`M@Qw@Yi6odbLQDp#41^tr<(^!c zzPjUUj(_1Y15;_Gsmfr9RfitAr{{Ah{<1Mmejzrl=}L%ceV2w^+J& z%TqlVYs4iGY#7CHY~GM-r_6}i7Mow3W$#wvgP7Mc%t`@2&?&t{5~_?N)!$|$WR(0+ zCNyZDaFQw5;?F_4zFZOasF0tpZvGwFG_%lq+6`;P)>1xp6%o2BL?Lwl){H&vM~LA&?rMn2nWW?azr;?N~y+u`k$dXAzz zQB8CH7WPrx&i2)B#0DGrQ`-&lASw!Uc(Bl$$Gvico10tpcsOyHdVtXO+ZjPWWEPKa zO=kqj;fR?vvV*e4m21&MZnCc&Zdu}NmmlAN85PpfY8suS;9k=-XBf&w|Lkn}4|;$l zrMI5#BQSStCbK;A1D~B;!x0t=%Nwcw>K}p_f5BamQY1npEPcDu~!tRfrzKiwOnlhs~7)f@eQy4ig z)c$Q{@RU`7SR4*!x|kP|WiEyr<}16bb)Xl;gatNCP!#qRFOibI?7e+2j(a7QovPfd zZ&UoD#bQ`6d|yDge7VP`;qqGDK8+*vV@`7Qfz4Ym$?NG>Dm|&RskSQ$mU@@yq(cIb zB3SGKOn9O$@ssAQOn1SWse4X0{q?mo$!$8BP=-bT?ql9o< zZNaBm`RF^QR-{s+*eZAenS0BghnftWAfeh)lE5CTGiUJ}N%%iqZRC--AH9OGKX18{ za@^;+)Y{@CUssu9%w;uSas#DIt`wao{N%-IQE{~1v z_WGjJY^bhbmsd*r9&rtS$+$_h_QdLf2o9wWOu@s2YEQq};g+fT7x#isT+D8FqU>ws zyA#~Am-#Z&W;U*d&-UiGfw+Pqwz1#Z%7)MN)T3!3$9RCy3l%*6btBHHLU6MhQ)~`7>F_+%vnWnvDPhpxkHt^QmF(d}- z4-&~TCwFLdW8q6YXFY1_i9Px)d!4c%E2FK}+MxFzX*PNe|!B6w;DVpxwWH^<6?gAmLDc){Sc0g6@22-X##%;5ZWExUZJ-bUgDmmtvXD^u9uqIXhQxoQi z#dnmC7hI0&Pl2p{KjmuUPc4J}Kyf|1`?M`-?_iqBkJr`W!3`}bqVgK#R@vns0h3vD zi|HZ{_7&2-Y*p$c4qX7C@C9V_%+^W@Y}W6T-)WoYawi(4`|fbbswcoVAUc#yryEZ1 zwPxvah1*hjVMu6U6UiE1$!CQLdbK)j36d=J0AS@RMFt07Qh#Gh{^0jMEA}1DIzk8V zcy&t@uliH?3q+DKmoF>489z1u_6f;3G7#0nXi+_r%%XrzgiTZpFO=|IZNj38Oy)h4sP~L`lHr3?{Au79z~J zXm<`4ql_o&jx|o8yF2Q8U^>=UV2Vd4L4y3ij~7QgO|$1M)S~mH3&OY_!+FD2D-+kE zg`7`lme|}z(KGyYLu7TS57F6*4Dz^a+(j}5Fl(+hEJKCe;xpgI=4O=+yUt*lkc1MeaKQ0EBI2v}tZpK9P4;QC9KE@{w+k_r2; zAZrZ^3~B*cYr}(q)zz1*VpCI7>k^e|XlPjR>F{Wi=>K^Y(Tbx}ISoYKpOUZGS=KCy zY2YKrq#EslgP)G`g7=ysPTz}P17RuM*cb(Q|zXqeK7sX~DBJ%L(Hrd#r zieWrSeE@9$U=USJO-BfYz7YX}lW~U-c?OPuZ^o`5UD=9Q2ku`#ozs${N-7F)#Vez> z1n24!T5|mjx-~?AJbnp~>1*RItU%{$-h`jX{&+Q8OMcvPV>t)2r#;28moFC^!Z-cU zg8Sn^`;KasEZW0LGBYzDE)w-hBmHZaLgAf|4<7zJ4gbFn2Gj{IS%TF5mn8rDvsPTX ziONc=A=h;z&K<3NeCGUlvwM++S{eLMFGCA+1ec}~1Jts7i(wcY8IKv+^2!Ren`ZLb z3ZxU>E{@cUfHNH!NR+8vsTI_1h2>Ah^~d86sB1-CYgfgnjsEmW5hm#!f+>#1_aaTi z65ECW(vrG)GpsIeYulVNa?xhGolMZq%=%`I!k5X(8YAw}P8DrQRaJ7iIAOo}fl>sF z&dz?Wqywrj`Oif^ODu|tPe?c*EG&GDRf(l%xyLZD$yZ88DJm;tmus=f-G}U%kH5b+M9+BGm|BkGBn@j{zkY?R z8S46WPl!~aSQL*rlBolXK;4z)`;b7J>5j`~N($0|U0@SX;g)L!KmwzGHu?l9DfEXV z06@vg%Ib~dO3E#_7{6Xi5fKrqlKlMRhYlS=Vjcc_C$F9kd6SuxbTCoM&m$s&6wa*G zY!658ZgX?9S56Mw*7`yKj4$oK>E?P#T-*wBU?*0P2gMGM)D$YT7&!bry9E&B*l?96 zI~m@IcaZe<_z97cN|Q|NcD;i#&4D7N%qJ!Whq>pr8kfi+6i>quFmCuox(L zc)_$wXSyTZE=)TPP@g`$Giy}7fKk=xbKXWWTfMKD6crV(D=Xiz+M#@HA-;fLY<&^B zJSU~3Kxwj|u8h&yzm?f*oEJqVorgj3aNTJZRaK4bT+PVHU=iq<7@8JYe$VLR>ste9 z8?XK%OZBqw&vp|{Po)?EZAGC-Sv6ku^5esx&&sd7kyqI%{FIyW3x+)C-uCxXhMc|@ zY_sg?>8YTkgoy%`EDEq>odF~@Pi!+4#qsm=qf8dAKO0ibgDov9D}#z?>fUkDns*8F z4&5Ccpoou?k~Veq4%)UDPnF-UXx3q;9nL8M_V&4)Dm_oF4#@>aJN^Ii|u{;K{;}MU*IG!B*o(1k|+*8*~>F4Uqa;7@GcF#HY5c6lZpAm zpkCW`RIdF%eHric(0B=$1=R5JcoT?28p(HC^<7o-sG!+M_z@l3N zb?$7XFe#gsgs3RWZno#a^71_-a}M?vfbaVu{tsUtkkK2YoM?vwbt~V27|scYVJj8r z-HyY5c^_DTaGn{sxsOXF5`zxo5sU;NQ8Iu=Xc!%IFn;~m-_Or-_H4B$p)hV@n~#w5 z+S{5M@j?sG8Hlcjk;@zoi#iR2XLxwrk+GbbrFJCm>_9XbVI(pr+^NnCQqa#V+3Jdl z9$sDtH#Rn)6=E!()u`938{``);2MDJrHzf#-!v<4cJbZ&f{WnzY;0^c^PkBfcW%C% zdsi7{-0WQAvelKPPR6N^3kM90bsZR2wZCk+6iPu59&B-a-QM=X^QVl|rnn~ zHBn9>df>nT7_Ei@>xfecS!_5J!_#LkkYf{#BYOXb5F);9FZV(xD$jCA7$$}iLqVVz zY+ANP`RIRMP4_LuF$b2I0`m{gApaD2#fYo$mZcB_B7?*o#BywktpA^ZbqymOj0uUsJqSvv%LG$T4= zYw2jNXdm>i_&{u`LsS3n$U>IcoZughzoKlE_&U>5efW9 z7*WRa{f+(zMN9zKcw8YYj6}TvO8O}^+DTFO8yB2dk^2`iYyFyr_f*a`Luy{xjKlD# zs)$1ZAsNIdH(0tBW(VH{dXo6M1whQJsa5m{N7#qIJtaX&26v(*@}V>hg%1Ro8e40W zWSICpd3$VUN*oBZX*cl(s;d`2y(E2}WdUgIKUbx|DC@N|pm6z6!k^PW-k!>C+NcjR zx|^SKlB-m1iy$zU;{@Kta{XNWfgi9dxB38?Xc~I&{(RFmHN&E*QT}|(A-QN)Kp7Yx z5qc_T)cL#+zdLrx|IMtE9M9+@{JLH9eum5|q)0TZ+ypI4DZ4Yha{>r;x1=85u2$Ye zB3u0&t@nG=B0jU7M-1hu#39h7nd%^ZC%oB{Fi3*sI64G~nqE)5bUg4(7OOlL+k3f! zO8{UX27G5~XXn*=fw_ZaHv!o4uzZ6N)U4HX!?(EQW^ZxuGFFMoKSOKhzxLrmB21+4q=YSo!ta)0P=LF!0!!4P3 z5;im5aH!&8ac4h2$MTbEPqHob5_YFn=Tra?1VL2c#V_+)r(Ba~^LA{U*`}>S_>S`LUx5{@fN=@AHvi-{7sF4m2lO-f{ioULWrQ31= z0K_kCOr{3iyPDz7ST`RQP&=*&q6!;}?)-o(8_LvGSar82{DL_xCnpmC$ZeI|Ev1;S z%^n)b(YAE%ZUICb*5o$8^tfL?RwYms&jA#@;l;uxjff%!}MhV#-UR!X$O!?f@Q{l*1U20c=NZK>_ z7;+F**5{N0`COlCj;nKRXY9Y#POSkzSqda%B8UV|CZaChb8uQMLx$1E`n>t0LdNYR3C5JY}j6)%Trk`_1lEBni1U_6!9w8b9-bE;I{qa<314j&t>o5!q&4-Vsh)PPxJCMn zt_J63IUZeTi4-=`FI1}k8Ftl<#k;=v2Qq{{QUJJpb@C>Z)ifBOvyts&#q{|Fp_yOx zd>Sw07ii1lT_JSNZ?xMuO1yC&2M=AWei$6Azb%3P9^A}a_W)xRz>ocjAC05NZJie~%-HW5gUIXOG{@8nl#Nn;$_{?xL-_3p+$(kTK* zMPX$_6{iEG_sSZ^<2*ZTQ@)akcF_H>2bRxUb5V+#F>uFgoYzPnckU_R(X-t8W1%3Y z!E74_yA|EHeo$QI2)IX`f25q?*}7c4*}bk$tNlbj5zs07(+-yxy_aHl!%xr>eVbJg zao7q(%6_I@Llsqw=a5T3O#o{D6TMfQK07W9m#X>y6Zg&j)Mbb(F4GjakDv1^`Dbf@ zB0eaVkdwpYjpvGr$T@HGMvoEKLrFjem^Hc2*6$AqKX@T74oJ!4;bFU5MXask)cl5D zGsh+fCcDcl)(4oN>%ZTjV+?m$ETQya%sH*g8~OQpS8~x``L&!3OrK2~g(bzLq*@rD zW5K#0A+#)G*c^H6v0Tg!yQLO4HvaU}>FFC96?sHbi6_(x_fFLDMvQ*pZJ6p}LCtgr z+;xnJDqWdso$OZIwK&dF<&Dp{I5l-wGJtVNywYT!Ek&B%dH)gXepymuzF2lPCL;aB z;HchILZxSC&n1}@WN8jQF1fe5{_H|2VSXPE@}KO9J(|wWbQ!sM-;Rz)r)J#T^s$}R zc}mJ!TLv6$CRfg>h=dsPySQ8=5%PB-gf6v;3!07=zqT-^?|{rwOR~{WMHZiHqR}BK zzv;tSh4`amrSWMF>2EUQbq-1S&mLxD!>_JBuvpjDi}rP*c>DkrVbawV@Im`JY&$^a z5aWyD+TY*bKK?R830iFuK#vZ!j?%no*P z8o5dRDU89m1etl)^4g;efpN-LT z{c;s;=i_c}>*`c1;afDis25#dpqcN_)Llk6>Rzhusk{%Z?>;4Ca#-nl@rm#kvsDNL zdG*^XzOvH0IPqbDi8YF+oOjT{8n4k!>wzET81ab{bp2w~A{97?EzgUo^kPy)qF5+ZB++dW>mJ0;H&{SnoUo1KzEQ9s1)#8c$+M zO$76=sTN%!y+7N-f8uoeJ?I{HRA68?_#QN|uWbo70Cy_TT`^7UNQDdApyoM-{xGw0d~f91G9?CXP&FO z2}%}5Z}pSoRlIRnvQNvFlU(cFd;&F{{hJqZ)H9UV=|vYj)7zb1Y^3aH9$oqRh<4s< zq(*$Hs8T*JEtL-Uggwg!vb#63INj5{wqi6x8oW}K;_875u6ZL-o1EK-2vD;&K+rC( zb|{fubYMr(iHOibfSyyab+)mw@h%i(+bj%H0|U&)$}a{J(}%M}z#U#SA>rOBWbff1 zoQx@igPw1(OL`N|*I(0`}lZsCa`--I{ymw2sCgx&GdU3F0k!*zC z_Dtd_gF)Ds>FGYpy_x%r=30j8)Sv4!qktg|`>NZKmW@kbVW{B$*^Y)L>$+QMq353U z=3z;iD#e-2g49H_nVxYib+5f$&yla2ln&0OcfVbk3GnHRvq)c{m)3`ORwWm`;Ze|< z-JtKGoM9_wp0x9=U0&wuRbagVn~@<~yOIF)>?a^wf?yGG!mb($*HUXx2~SOM>1?yr zNgr)&um(FyB`VK;?%+Kmb0nl;i??A_P~LXv_#P!42eleae$-=99^VvK`|mrz35VEyI5-bZJ^=u zIV{ovAPu~@L14F^&f11MAgt8E!9l=#9mc~{PL3l8dyIf>UQhO`S46qFxd1M|Z*GPI zCvyQS5G3Z}x9if-Vd@#5kgJp>GOZ&oP6f^xvCoskM0O ztj)?CCt<6DHnvi_x}o3#ub8#E96abSRxp=n^;(}?c-Kb2!z15eCd+D6pgmi0 z@Pt@;Yoc;Lwsd8*pNGe(XBUf82gaM958yz*W9i-x=h&^4X(cgz`dV{@wqLg8SI-Xw zy!g_^c{Q%Be^jo1#`0;^oFFp*ZO;DuL*jUJ-&DSH2ht9%v;tCTZ#7bl1sqB$z*vt? zeu&~FOqGkdbnO~GCnx7CovH&?y@15!IP{lWzj;9(N8d87a=1Sal%}heWQ>sp>Zd}gNPel8MfLx7I$gL|$8-OywL1T@*)zkBq_1ePRUGmwa!P4c=FTo)VyWZWq3l%bt zKq?r45VQQ|C@LQi?W2kSd)FC2PGWf~q#$o*FZ<=o1K0b5cq=h=GF(jmja;vQTmc|I z+fkWuTWfNQdT$F+BS77Rya59}JqA6n$HspH+JK#tVESAFuAqIyq{;xRM$ga)XV2ww z`|;wN;KQGN;bv$L<_i)#N&2yEMiQDh#lnEzrlqABy)qLcsjK0y@uIC474(XjQ9CluTTpW+pKaiF!6yOY()gC<^hyO zg-axc1U+ysN}E=k7ncw!3aVM2Lzfe#!Pi(MInKsD4c_B2hKE#OxK(){lJ;5iS`)FwiX=!P{O?Mtim5)2o&BDwq23ps} z#pNSVqoet(su3NlvpU_mxU?h&{v<3?ok3w^#26IPO!XJBK@tVYOPLLn+{`Nj^;hGK zk1$}d32B}}_PN*9P8ro(YR?85_%=61P;9J)JwKACf|lMOk-4Vbtk^^k!G?vt)I@B+ z%{COL-so|vrq7Q)l~sH5GPHOw#+c}e{2(kqM2VZ+_)-Stn6Hj^XVa!CZq?Ol6^?UJMNZskE_yoJP?JfEo8-MIs5Q^RSws}bMB{xprMQyIliNBxaBFNa?W`Ni#qO^WYelKXMr!hiLRL1vv7I0Fn zGDPi0*>Ps(Tr#1OsHT}aS#G>f!gXrAi5+Zg-qaXq|6iktO}BGav(I13*;h8L*A3{S ziqU)9lY~;-w~&re4H{cid{0*^T?@0djN8x&+fj`yTSea|5#Tt_EtfQOe$ZDVprw_6 z)Fk&KdRP$_;lwO=rqrw$lBE_@32zTZ-xdy)9|4^q!QK(m12{4s(lMrZ92+jHc2{RU z6GCLTlPyn$8ooY#C+RDL)fro^RQ>_ol$i>HO^EALTT;kHqcaLx4gNP##<0o!!rWQy zY&WO!0KS<0c3O6`TW9k7y2ykt9`DZCugdP8`>cwLjAd!e$#m+Q&*S2f7E3Lq1^*eb zj1CLCkJ-swUVy2K6$;=r9$?&$ew>y@2QHKj`v$|gb7G3E5R84y)pPcp%N}Q`fBS$M ziB(drF;bFHeQABL$#{1y+FI?LVETIY#iHp~m+eI@NFzq$Ob6e3f8;AOav=iN5P~)M zdXr_&5H$(XCDqjkB4lPhl@0J|-UB`SPU#+@hlvLzk=S{g$4I zRK~qA>-#q7FxJ9xX4?^q$Kt8Osj)olgiWr7qP#L2Ib_MHZKHOOa)wcfEkz_P#*_{R{3|)xfQGI zRh$>j&Zg`TN+s#hP<`ozuix z5hN>Y)pULjx%ruS1yn|GpuU?@V^rRrbHX7LC4W=6g$J;7Ws$F*ZeK2Cz_KIDw zUS^mI4o?hy?ms&eD+l8wE306?)3US6eU-JEc7I@+aI^oz_;5K;=R8j3a`=DkniUQqtg%x6AYR|5AEiiN}@O8Ti z#zBw)pMX_KJ!Z~rQuomYqWuHS^Y?n328ykL+fxYvX+ol4k7i+3NgpNEwEX%^H^$23 zdQHD&z&jPD;91spMjZ^3pOqjct1DiSzi`%_dxEM*U@~7B?X*{V$^9U~qSXwoQyQb) zg4@;TSm8uo=v^%VekW98;z~$jJ#K=IcrdW!swnc=?rIw@VFWEEr?cHj*yPd8NgPHF zj>hcexvvuPQG@E7ay2eHPqMm&qPS==Fa{mWw~aoZSM`<`iSdu8V;OWZjUF)Q7YM(G zPp4*1Nws^A=kVch9ptP;aKV8%uhBzmAd>U8S=E(-MAR$>6A9GpZ=1!yX-1n6Bh&~P z(I-W8qO-PGZ6+zU!3_vO8@?YlC-=YMJSA(x5*Cj2nKFLcU>RIzd*ram{`yH!ni3LZ zcV}-UpbzO0Hb8{~n53Rgc|aIc3sw;p+4UcuA>+eBts;>BI5z=m1lU;q6tBc20w{>d zjow0k10~G&{xNz>Z!rZRR?1YnP&kAD#8(=(HDcy;|BRNW z-$evg^5mjMSAsFVpbz-Zj2Qd~;I}^%U*I1Q-Ag;PJArn%exrE>zrCRESyo$axPh_t z)bt10mrwsh^V)5$5z>V!cK^>n(SIYSF5aUM4>O6QBJ;_G|5B2h^d!XF#=(W%|!WMDsWe$>Te3zs4pKBa&E~HXjUk*?Tv_ofzBkR+xBb=uesJ+2J@zKQoF{Q zev9OR=XgVy^a9ufNYy*IofbnCEX<4$&Bn&Y z;^N`eT7M9FWHnZgz5@PtCZ`gbw+2uPdeaCwy=`wl1}?Dk+H6p0u30Z96fPgc#@2=2 z11R?+q^xvs7vNg~HNp-n@cnxkSW56pF;L(;!$`7^rS_Iq13G#I@BIndPn;X$XhJ_<(o6E8zYYpiUaTgfq+DZ zTe-*EY`dS;cP^*w)38M!<8x9E2R;`4JNn2Ol=;FT__b zG$R%qpD-}%&BFp&AW8*3=hG0TN^d$`U|;!Z6#+s5gERtyLP8)^8eUjnV`c=90m=)* z&rv@cq?5Rt$Rgr9_;>j08aSG!38yzTh- z_!%}fsM~`-fuRaFZ$8v^+mwvX&ddzG%4;`pP9+B+_M&4DPKZ&gfSc5iSK_36%$A+_5&4Gcw-n7UJr-H;cfPq#{Po0KL@7IHslC?<~VL11enC6 z!3l8tV}j)d1_p3L2xLvj3sZnimZMb;wjLNcx9!PoSIbp*)4b_&z`4ov6IH&5`~+cx@8 z%$_=LmHq$|=Mwlo&o4C1k9~fo*Nhr0{0>({n{muX~AzO=Li&yHYMd_a@M<~?$sXu;OP&jD+aS_+IzrP3R zwzHtp|ICs>nycZDwA^pe`hQ;p{B&}s>DD}RBr|!oZbzkJ0hS6>=T&HB0#vM*=_;<8 zb@C#+CnJ?>*b=$WB}nKzW65j0k6>vek~R#|Ejn2kAVFM%+M<=Bt#7w7Ud4&2{hw)DDL-p@}zpC=24w3h##=QP=HrJRdTgH zXG6H2yy1P&Z5xXIq<9&Tbl=|MeQ=PUw2swBsxOu5cb;BcK%xUT9dDs%B+L zN#7`|=m4No4qt+#iAYJ&9d`g`3^${sqI&xLIRy&T2L{y4pHcTO4-kZ(LCpTjQMoVRE>)pTUuc+lFwo0G& ze-U7Po1j9R<j~39K0CJsL0b)$jMQe(e(x9kO@hK@%_ll~4mN-yy zVScdu2&FWyjYjz%MClwzyYqfHIVYc3Bfwk@U;LOwCij$;(lT;3b@u>h4tu<5|ken zfF2XhMk-h4_g*nb#~|Q9^I*!{xHJF_obUqev> zx|}1lL>7F=>wmyw7rXZyVg+^O4ILH+%W?7X9|GX%dru!_1?hcYHh_O#@)|Lo3>f+n zANHO(+&A1&9U>(a-p}$`(IA%BahS8myvN38Sg3Rq8g_7vDqHtjZzbR8XckjwWB~DnOYkrTStsRe*yssJ{=Y2B{d-D|FQ^Z6A952p!K)=@{L0a`Iw%%QYkUX zB`H(=t|+r{)MQQ$M;Gi*d*ueGe~W>(N72WSqB=!*q7JPnDp4f40}hsApYB|btZzk( zcp3?g@lz-cGyW(>k=QgN%UlrJeLA^AL9p;s8nerC;NL@M23 ziY8wl5CdlvM>gHr)KWX104(#YdyL^b5gP|QW~d2N|H;C6wKBW$h#-I-9>TEcr{Udk zx84zd^e#|QHvKj7&S=7l+gtv<7d3h^@Tk4t?JNYdBEwYsA4fQnZp8dyfB*ix@4E|u z4xf8Om1GxgZf;6}3yyXc@VS$}X@$jE{`3pb92{7U8};>Xfv-&Om9ImPY++|hf=kbW z2jB2%zx~LY=#q@}+qHTjAW3`i#ncZzL0auSKhRIPj#R&m+Vs#ty9Z-}p20Uv5cCtJ z)?h#Q>nqg4Ay;tellXO<$6|qnjQk9IA0KXy{+rC}D)dhyH{pT$DCA#p>07XY355Pz cQt!rnE`yg$WhaA5QSj%oq?|S)im?z>-t}NkL%IwFCu~4naypknZl5RtXVl5$WzO>F(}sq!;mw z<=(pW?DK!#=Xrncm-ov#?B$$ejyc91*L~gBm~W(?isM}*y$FFo@E$!x$U-0(PzVI= z6dMctBGmyQfj}rBj}XH0wx8CbZEUa1AD>aYGaUIMg8jKmbdxCrT^LiCPzGOdH|~B| zm;9A0^cS(O3Gw^U-4qrnaCMENdy=3j#Hhh{hAGoR_3#n)BiE)=B+W+1qr&&Yk%0$` zr2>Ot!;yB2k#=0Xn-%Quv9YN<2}Q7}FkP3_AbS~r$?{G z%c+q9MhU$6^Fm!#n6w1PuczZe^Anm)h3NM@f1BUcC*BwmDYP&D9s;`#EklnD+{RB; zht`eWT?P@kfv@-A{j{s>3eL}Y)|v`2d_+B2s>xI+Uuc)vmCzeQrTZm#TYC|2jD!mb zHfWx#ur9wlb!bU_#B4A^Cc=tKl%CVBiuXzI>5^hXelEzrr?f2(CnO4UcCPe&^QJb0 z%7&WD#-;@K;)Bo6p^=Ln>(xu$2Z>^V`q&pQ3at(VX4~(zmJ&D@Ho3=~Y;49?EpPqta)>ljX?xSx ztE#8$x|Ko*#MhOA=JyoB^75FeN?Gn^v*fj549HNs3l}17w`wL>qddCOpP^SCy(($1 zSufjsim1PgvwO3yr>qg!YL7ID<4i**&E31`3Ta|9rw6*4-vVTO18_Z>=X~~7bxR7?It5*PM@^(e zpFh8Ua{5GXtQcS5#55>1!|r>G==w;`*7i1i-WizN)>7iBOojc``i2ME!?~}m>uL%N zo;7uLA|5`>mgHC)$|3AeRS;HDQGH$(?q#+5s^;a6sAilXkENdXP2Ouf{N=VPbk?Q&EK;veAr5ixO+ewt*2HQezK<798JNN2!hy&#!YrzH_}mGjw=XujcawHCao zeLQfbKdo544ufE~w!z=QbZzJrSc?nqn+aA2+H3pMkU78_A3pp#Z5gX<5DYvfHR7kE zbu+-87iV9Zbg9B3rp0ggDsw=eWwX?UyV$uY&!ox*x&KE)%t(E0%38UBo!wM!$C;>J>N zn$iL9Rs4?V(fG&LuQ>;HN$2&B-iz{(w;E=ntk$qvZ3RXpzwY}mQdL_*-HuKzFRxV{ zn9g|Fb?uq@_+sS7##dFY%*U!pt1R4ta5!NEi%a6;Sbk(uJM^8CqoZ_&%2nK(BUx>A zvqAbzol27}d)tj!qKK|?JM=qGNWZK|iaWi?;73I-^`8+25zGLsIx9s}E6uN~@knc7 zyiTfL$P2;6!4Z>rYcU-+CsY_mNnBIo_S7U-Hp9pW+Q>>P(ky zD$c%_I`aq(RlLEzaGaqYbX-4NCD!mqu#q_naapm6&F~O5FfaSL~mi z6$m*2MkAA^i0gbbuRUJ?T(MzynI<3$AWGV9R=oZydmC0} z!=f1OfLzDXA%AGxolvqZ!%hT?CdnAHO@;(+}zIe#!HEEa}`HmdQ}G#_vN$HFZ8D= z)qtFmmI;TCbbyElx8Efmb1)$QQ41%J8=Ia!BijjCma60Dw?CVbj73dNedX<2t>VQP z)leV50$eDsH6sE9Cqfv^H=K!Vs^S24PT$j$RRJ7LK27n0LaOI%AlJ7N82(twbXrz; zf=(;e*7xrUXXbD7{^Mn~`@%4!R@a2yDf0b4B}srQ{v&Tce1Hetjb->FCH)`e+kX%B zUm$?-Ef_53^zK;^|Nj6xFeCyQ!L#Kh*}+3Y1yT9`rSIzWR7(I(6ZyS&;fa&C_IoS5 z{T9MVh%G7jq$L{=%6`EYXwtVWpBN0wEH^OP_g|xdU@SpzrS=atkp{>uv-KbJ0{*kl z>Nk8tpA=fk{42i2g`5h}Cr$iHcW^om?=`3d{T}nLVT4oey%dy| zNU;H^>o1suFA+$E1XHV!{5cgpxK=3Ta8Ctp5EnhqUI0TUxst<7Dyi z@n6--I4A5^nJOgjav}OmQZiJF62yba6(ic4n}zM|d8q3#T{JXqAr!v#sg@<^4cwad z*mPkDC_vWGQFHCq*h|*g4;?Rgl)6;Qp%$d&uX#?zeoK){CZwPU)@co&o@wwu+-{@* z0O?X&6qnRyPm5R2bez#>Asl4nex1$IRpz!&+{BlXnEYNnsg3Q8`9`evj`eIoe%^zn z1(v=HU3JR|PB02o9v7Sf%POn%H(cG4(%xO_{vhB~x>8W#aFF1H+~-k{uMEY7YB=p( zefR#o5i{A9E1v$>SyGJ|c=l?QCWn$kBcha8ikkh>U#m`SYz0?XT+V>ou2ql2Z3xb;blLW~iV+2C_5=-Zy(z zR|{D#b)oUvY{-YBR<6qVl#rY}h7bZV-`LuaMbdi~?h_u)KUvr(fW zDn>?Jr{mq45{pH7D@OJ58!|F7zJ7kS;uKDvOsa*HckW=_d5=TkxaFqW$}@5RatL&9 zrJo^hB9dLVtH40X)5}Y%#{EL7DXV@TUHSfqL8`SxPyTDU#~)bHHYFX>DD0^dly?e- zZFYsyidR}Jo9#Bo#-V!&ckAX>{Ryzf)7p?0BfU%Sq{;e4M7I>rCsOwo#=40Ud2G5) zMb%A?)}X&x6Q9m5`7izuF$n zdwqC#7y<#y0$wcP>sM@pfplRJ5%=7+rS3$tr7o)H04083>}(v$(GIf%Xf-ukGjMm; zvi$6LwW+nWuGZ_)J;y^+g_{KYyM*Qz7CEolN%M`zN#(vgu4(YUo?|@D^WpKGqHwGH zO&YNPA$4{2$UQ6q@&I6R)PjN(9#8_s3lN8*D`?ecr%0J1V>N`IpqTZFD|lo7%^3T~ zdo@bOEpEP=#^)1FF~{E6?9{lyi)40N`UOM3 z{iMJVwh7ZmP^SS~0aeM5$_CiV;e3P!?@-W%g(iBhow zLxo0U%xa~dRf>SQ6x;9Jg;4VHl3A?|C@frnLTLmACyfIO3PdEA@WO?gUq& zL!CXC_>1`}Bm;%xKyA%(m51>1(f&e%m(TQx|12mhP3KCwQ|+$53J`y38nQvLV-JGY zpLSpDc*N#9gJ#ieQgeRtUFvZ(&(+i8J<(_$s}|f4$%*6l(b0h&(Y$q&lN7J=NRS7c zN(&b-F~1tW**-W>THtuygDH5r`#$x{$%*63moL8#uz;cha^>pPuo6<~+@Kx|M+c5k z)q(EHQI*clP6DOO=V*}Um5wECe3B1y#hZnfGv9hh(@;N;EWC!F*&btPRav0JBpBuB z6jtSKdDQ2kswJ0HN6N_GLUxQ8A|xgzXl(3x`SD8H4Q}(#Y%F|yFtDJF zN8cCk>2*<-Va(lm@=+7a1(;0gj%lu!IJq4rlz<$erA4*S9_0hVkn!9Sh)FtPX$m2l6!f1*{zrQmwh!mOw>CdonaD*o-ofHo0vp|Tz zBjZFoe+~nI)5ylP+-6f96h)Vvot;@D9|LSw!L|tuerdFbMMOlT>&p|elf&8IR4bd! ziNK(nywq^`4ICUC<#O8`{WDbTOH*P2(HI1O4Bd`TInTuHyfBpZE9Q1VZ0gF>m8x%O z&;o0hY8Nbc27lH5Q712&UAJRuK-I+Snd7uv%H>oq@2NM;kLYaYxj{aQ8E7Y5Gm-r= zjt6yl>OlH>PoGAB8rOilRMm>CJH3*BOMdXmxZvFQz4N2(!@UjRVm>GLjRF7w_R@d7 zamJ{}!=pTh1LIqFc+RGFb}KfLOlb5q*zqIPPh9YG#RKZ?E$;3YmJ#cU1r1b?gIpQ& z_OCk0HCYS3#R$TEeWep}jd}KsDdEi*-@a&Jgq-hS@{My`I90iiweYy6lGZ!^iDSS9 z#TM6!R0T!7{$!By{CnRg{be(YyyP?em=g3EQ-7!Ut^2sp7@hA6?Mnk0QR6y%MEC;) zrBJIh1a(&@f!gX|4aNId$iidcA}p(%`L|HZb5|CE1KNYeyN~L_i;WL+wM^14Nti9` zqyGnJIU)Wyzj!ux))azx`H))RO(jlQ)MSuUf{FQxKKehfK~M63Q=)>6@24O!P%qKn$x?n_{tQY+&7IYrAo}F)^NO> zMCp^hh5f83VXoo9Ngg$KV}lOUpAvi`7wB0hzKHael?VjD^)Ips4yiE91IrDTrzxbK zO$o<%&a<{MK2&!0Z*1a9h%aQ)wnjvg!$~>3px{o%9iMFiAYkz?tcM|eV09pPiyaiR zDF?qG9==eZgNW87_|5DmU4{*f`^Nw8yJushhqyvz^?%WFwP;_>r0CYjB_>}S00>LL`5I9akA7pMPRnRUuz)Xl;N|p=*2rz#9ywwn z;%gWGL}h8n%m#t+o^0|$f#bN1Oehd}t9G$jn;vB;4 zR-xk<@3UCYPfxny75S`#9F-%vQZ1n6*1A$rdz=~MfjHy5X~r~nD_oKe)DW!5AacE{ zN)?Zz_E++4))s%FpbBw%Cc$5D+*A{653dtDV+zy6k_`iM+l^qK%c?0@UlVz4>f@6s z>iVC(XFZP@y|@46rA`ck5dhqGoUMt<8!<#Zy1!^KPa=*9H zJX0RR2Qy{KW%HCOc-Y!(;%stOg{Hu=5QQ3ySsr z7bY1?Rn-?w zWO{l)1O;_a>ylE*f1PNox0HlbKEr+TP9S|=Ox`F9cQpSVy=A7-rL)RPRwPpUoVYI3 z&RIY}V#;(tjRqPohA194iooSes&mWWS-*))%ezz4*(iQ+K%SI;@~iUu+bo~7H<`{` ze?SfXy?sASiB?6eGt3mV9?!X(fd7Hq@in|^X9dAXW=c!0Hz-mN!!TM@IPR_#Q+MKE zEWUSn)SnGH+xc2*l#g7Yt%lBVp1a>5Lj9xbR<%%a(Yhf#+h(ovv4hGU;r*mO}tUWbET1#qU=O9#CFpEGoOc>N4MmO3R986IBJm$Jw6?Q+4sN z3QZJV9aX)O5{o*#(f=K?Uua^|HU8ONA)VCrdS=JR&wQT@BL_3Z7%QC}zqZeAo4zo3 zTz(sGQji~pn%t~hOtqv_XSmgkF37q6hwZv0kojs#k`joRA|Hsp?)exvs1S+T#aG z2CCl_8!Imp(61(CXjg5%(b6NPNOW)l3-c>E{k$JU9uJhSE-m_G?yXJ*H%l#5R;iR7 zy@hsV2&;{+W?QY5aeVb>3g(OZF9ZB57yS>}gyh{)|BBNmdO(@{ici9yKwzC1j1q4{Pf0Y$_|j3Og6|&GNWZB zqW^{|5Gq(b2?+b`PP6R=auO-*ztW`#989si#H75$8c-x+KjV%mp)QD)TnB+%gHP#` zI({{J@Mi)XL`u3VOZc47ohuv&cVa2BIt56ll)<2ie-1jf9yROXJ?rmXku&kh52Wn9 zQE?2IfdqZO#Gg-LaFTKy4uhXP zg)W$!>~E6`AogQWB4(C~b**!P&dmCDD3gVFD!5F_<`47{t$N7fn#}`diJ|o8;)785 z4X0sTHLxQ97O2*4(x_i`EB)SWtpW`w7C?A=u76xOFBU6GXU9R6C`&!?VfWPFL8z=s z_iqv9-c!2yqw4{vvG_BNSB_l(i<_VD3(|OFfQjcHPD1U^d|m?hxb$oJr>k$!UdcE& z0JPF?77jBco%KW!sK2vTL-)Voc%kUA-zcf<*8#*A0iE|d)u!1Ch6H5$@BCKH7cfH< zJGG+r>(L%hz@$)g^Y5fw)HirMsu%e?6UG^6Ch~*n`i-0vbPk4uI2S^3T0L zwEzd6vbiQf1f)SsX5%3;DHz~BGHTx0I>K4Cdtg0We%7p@(+)7mZPCjLN;r(hZ`Sn> zHLL%8RlwoeiEx)3F(^>dEt6IS9afD(X8=|rl3*xAKt>Guqfc%!V%wxGd~@UKUk^>Kkp^IMFf6W-CkydD*=j|6YYNxXjW zTW|mWRVh;+>9x=n>5-p*UxFj&jVE=6y3vX-QWADYDrT+g>~gY02B4lZMiC-bfmxnoilF8YDl!*G8>rwW+4w4SYV99|5Zh7h11YP7PBg59s10 zWQEz1dt-F|MU{aI%&uqQLdiG{h4l4P2faPsNx4US8t`MOI+5+Kna`Q|5FkfG zLjzRCW?;BpUgo=L&HeX@-EoRf4x=9XzVWMdRp&Q-S!X{k=xTz{iIqHguA($TN6^X( zgDIEbMynKw#8nB_ii()ZAq$)AT_XEOjBXNWP*B|J^KER*s#w`YY7O@_Km1BW@IID; zv@i8d63gSec{hn_D(C&((jPlk7|KM>ZmgMKBkRvhDtES=g{H0_9Ue{vw^NLccQ$29 zf1Vtt@QN&h?U!Wi!`91a*4I5ApoZ$t^iuPmkXbNs{?3taG=}YVcJ}o$ps-C=5_Wy^ zPS*@ILY>DWwO7|RLo0N~Qfh{^wbRsegH-KKH+ua*hW)WLV#V*I$)~FaEbS5-_TAmx zDXHORGnf}IT{6i>L|Hwnt;tP!PeuLEV3c&x#!&QI+iOCieB(Z$1A-~C?vdv4e&R}^ z+L{8BgBP=_c_AaII+6RNXZ%BB6-#P{E{jaQ*q8O|RWrs1s+SrI7^tYha+>Vsw_=zAHtbXdouhsVJEpDQ#Zz3TMw1tmj!r6i_$_9|XODBO4SM zowP0^Fqz2uY)WIJ*DomrdGS`!ba-MY>6=;_K0jhKz5nF(BM=A6ZcxKs>gY^=i82<^ z)1yO2M+a&R_H>P^3H=*%r|W*?HK0-dgf*(&n8MP*V-HY)xHoyO{7wHm9gO?*CQGG- zN5Rdgf%qe6Xh@k+qY?%If`NO$cTjBuKyou%e3CG(+pz#$8g~y5(2HeCS1rnX9DSWd zy&m)$hyYEzw50brIeE4xNm6l?v3VdvwY$(r6)K0b-MoiU(Q`L)ie=!6e2gWx9` zZf=rTDMyT^7KG2MCsfqRf(Bpu32OYys;Z}Wjl~gqbO{XN)TbL~#k>6xLIZomdb0it zTU?QZsvH;D#gk^wR2?X(?_bGLNX&k5{GFMl%;e}rzM(Y5*&X7Uv^mw z6R4?8H9A#IkMr8CSKC4mWlnljqL-dNT14n4^<+t;QAm$HT*wrt``}$Y^s*L$yfVEG+TH zZ|bAC&|NVA#y4L0ns#NX@s^+NkHYPCZWWqL;u8r2LYgWIW;l|M)7{;DxYNoqF0izZ z)gPGMX#wBSvSXGnyr1{r3fV|e}YAc=hpcsUin;9#_;!<&G zuyWkt6#gnTSxs#pWE1T%QGx_J5&krzE3Y2%9({(5=EHs`;$dAbgm}ou#}k zDc+i4cl5v)JH~O#8F*#jY9H7b6Mv@Gjc0rmi5q33E1V(o_VDZS>+u{xabG+25 zE;-gY)p3f19u}&^3RxovVLA_ErloC6+GE5xl8j*HzKL)6L^ZqbT||TrXh(}6q(Qr| zY!y;H-x{%fv>2;B*Ywuc-yejUNAK^X?)mxoNlQy>7Ic73oLgKJMG$H+lXvAC$ihgC z#>HNc5YDe3po+c0>ZOYnFwYvw&Cfc(>6G@5J{UZI=9hsfei>v<9 z7OW?ZPH*#8Ez+Ep^-4y0l2WmpuEmlb3Pn0S((fKyK+f;#M)?O@jq_LsCsv@~2UGO8 zPpigeEzNl;vDPbg0jE_fuw`5YCYOk0z8F<(uJ8S`5p6kE)-e^Nu$yE`>K4Bx+7IVv z*S0{v4{p8oVaH83)KogA29hlZcbRg7Qc~Lj`AoKUBfi4okZRVP(F4$UR%*X=gdAO2 zTQlru$0%IMVKto+^1U*tm~X1Jjo}NLM#bY+5K8Wn!-2>^GOihrObLnl(w`AS2yfoR zBdUqvci5V6*i0G8IpK3Tb4-8!jL&y%B7r|7EM?5SH$&8+U(^X09ab*ey=&PI#Y<)+Nt$u|w)2ZybNubL`YIpiz+#H9VqCpf= z`jUo}4k;nV;g}&Jx#l;k%BeXzdM*qUgr*iR96k+KKClxS%Ps!_9^np*Yo|}SEexZh zf9AhTM{*ez6>p3gW^ZgLhkl={@016DK=tQG??VarRJABUPn6m{Q9RgQjTHhD4B;~z zVg;SO8bi)R&=&-m@^jtZ(@$_W0RL&gr9jl{{&1U#3c_kK!3UI6ctAP<@?6g7(?{m! z=B%KrI`1|ITBeC{an&0eZya z*RLCQzN3)TwQJs>;p%vA`N_P;2FP&2Y2J8*QzjxWmFbM18U|x9_@Cc;1$^M%R}+eP zxAE?SNe}n7XrXZH>8bW@Z6fl^?nsmMX-l#yryZ(yD)+@d45#ynPqk6j44>xV;v#KU zFTT7A-Y=LSdu3@u}o*X9S~%zsQKrkD7{aLSO2>acqHoK=KLy2ZyuncLjnR1o(TqnJlk6{+S%+1x(5| zLot=n0pIHuUm9$z5t{YKo5*qAHkgKt@iMdzzXa0s(4kQ+sT)wH^S`CB~uzhf-a9 zEay0+NfSVHUO1rSY`@0o52AHmz>aM@;0#ueW zDuair!oSjeY=4D?fl>2EX(?+wU9E!QIR+@M7*J>fiVV;cGtA(H|6>GjSU|Myk6QuD zhKpC{wx|PG8`({8bMd7*w0o$w?*H{bQ+Ip_D8x1=Du_r){XoxuHlt{wr|h%2C_-Lv zw1-&v67+nu@DT?Sb*T%s(yiYM<0-7jte1c9;Qb1;G?9B9pSV3T)yk+r z*#w}nDK~7)el=sL)QSOuD#Dg}lY`kdPmaD9gByroumB(!&;q)JmU1An*JybNfRGiBzYhMRf-5HemO4uQY8qHJagRl&nV82yIb4#YIpgQpM{38 z9Mb`aqB1N3_;&;a9a&dzj|-Lu(*5!3lyr0tIYJ7=0Ps_HIklRbpVtEF*Bb(k#cWcD ztu9dlDlV?;DD~9pK*l|tmMhQ8ZHd629?dV!4Tir3l4-Ax6wuMpRRhHoDq`vD*836x z1r<<;fv|PYYDFHP2kot?8fHO@tzId?c)(A9%Y}e2VEcQgAj5ZWJTh;fUro%;CIo3w zJnvO|s2vEn-+*(O?yV@a#R^hrHwGyf9iW6?XOaG_x@$LZS4PE;su`T-+XH!{q>gijl6zYCw#BWbDO zPRI8lQ$QmRkSM={BFAL3j->f~%hjm$P<0j{hHDI_fI?v0=Jgq)eqsSP+(1y~J3E|B z9n{v=He2qwownMSs&EPH`31;JUEM_E@zT`wGf+Am9u?qzI(@+a&{Bfq)bJ%}fwp#N z+J%y`LYhjIe0}cLtJntXT`&GL{U;yq*GLJTezs=B4~jU@uOdkcH@gWZ#=l= zQb_MQ$ez;xCqY~%UCzhX8K5aCDL2?(1+46ma_Gl_w79}!W@aWqM-P;KhugELRXaIq zNd795@e#l(lMo6GCY2KN5CHGw(`1KJ_LhL&^&b{(3JX*h4=gS3D`q}_1Ayz=LeWEZ_?eL%>peu{B`@Yap{%<{0zjbeK@7uke7v;x?bOGfD z%<~wXbBjK5qXIHtB=L8A_R2(2iYS4K3}#)UkJo;q^t>^+hASMnc&yiar=~P--MWPe zHmOHIS|psMUZKeu1yBRQCvFR|$Yce$a+x&~DW{oS6sOVFY_MQz zA+NyzBPJ9x!6c#sl%*v*ZZ@4vpbN~Y??W5b za87&lb}klL=jQy6g*9ZDhE&W#1f*{I;l>6j)4x%^yKd@|_VnX5iL+;{uqBXSf5z`m z_|OMOUhw;8OzFX;7f9au;y$Y1XAXXmLr?VN)-J_C8`?JRpGgb9PPjP@Gh7<*=1q>K zYytrrZb+3cXEYQK@5H$Mk!u@_;c&pWr{k@=gHiS6a__>@_oZsiD{^7l2FaA7m+uu? zZ%-bcYqdB6jgj*Qw%FcJmD9^N`NgkG39^v@`i3C*@j||LZ_|q(y$?zhE&%NU=GgX{ zP7B4E_hL5Fbsto>WeiPjQ>JqyZ?~XzUs31YD*w@uIz<(EX3K7K>}B{bHZk7kaOPj) zymN9X?MbupMyoEqul1iOpm=Z4PeT0(SS?$rk?&boiJoY}e;=x3+Qr@4B<<TO3 zD1!J;#`%!q-J5tI1pDpT^hKuC;4#+7c$AA3JB^Ez!*cY5>0T8ub+8O?u#fu16lqw%Dkm(fCP!APZT?%o!s9`ev^1F>9BHLtA z08$Nvwi)V~^_85dv2o?jyQ3!Zqu;l;Ybc<3Ya6;R5%3-;e?aYQ;~Ke~!#ekfjrhnY z)E|@|fh)9{gWkcRM^Wq(;-leAgf|FX@4kIooHv*u;Q}A+FarUr`n#TAtO{oWJ=|W1EqMxD}Wzxm~9-YbY?+W-EIoCu421Av#bv^_( zJTT?aDq;r$^x+k^YJcJw89Zu>;1lF)`4->Q=OhPvWyHxY$La+W^|Tb?drNxt zidjMP7L(|?Qd+AYar2Fa(HEvPJ$=05bqAy5C$FiF<(pq1<@y@D06^r?Sdr=89K}-? zxKdDntFPvHp;T*|1t$!4GQ;kRZM->*(Inu7?;=Q-@7zqCHflIodKG~=xp{&Pex|T? z!XXk~{2Y{8xH_2Ozw!FD&_2E8>KF;$N`JwzdWJ@oK&j0pJ_HC+$w=?(Nq^aIBqzlt z60Vx4prl?J&C`uMl~@navk{bXb|Iv8=ix9I`OQ3lX9_4mvHje6iip zF}8<9F~Womn)foEL>L#@&3=QQ^cw?zq^_+4?(YJP>%qE!C4vY!N6LU-cU%N75($Sj zJyaO2qj1I+c5`bw+N;98h>JLm^U0`GLis^l93=pvpt8YUUS3XBxfDzxI^Tqi5PvXT zl24X~RNX(Ndq8FE%(^Y%OK`52NmMkyqdtLOqz(=jrV! zgfKn3zS`Lc@9nfk)^<3wPvm45IOzsuRM=(qO)U)#jb;|C5Ay_t_T-|{cK2Y>E@;qCEr2|elCrXILIQQa{oA*18)(l?96`nP?8l~~062*9!U*XU*(A0-AFrzf z`8zv1p`3|{i5Z2@-l-awT%c|^pf~`Ub5y?toZ$J}zH6zN!AfElZ?n;b+VPa&iH}-lCH~{U>ZfP($kY5 zZaEDhgzb%$fzWYrSAe~J!&?!ZJ1%Mn*Ew+9xhIBq^M#e>8^ggmw}QXP zU8p}uf^?nOVA~0aUY<`9=!9Dpf{^~8aNRAtLR-e3M0X->Kin$$ua;Pn1|W9n6#zIN zdk|r&dF0JUOPjnVww|s_!lw?R@Up3BT6pb%6G1f0u&(TE3u zQwH0UUM4-C-{@C>y}okgigE@!I3ICzbOd}pMfL&&)ok+}s?2B#Q(1pfmTL%FFM#Vz zsvG8Hv_d^3$yC$mJRuE%hH}4P{`-U=blwMlH7%@F6F*mXCaAf+OK%_-E0HD=H zL_ss|Xnzz2!+jYmQl`k{KS|4J%phSrNlxwVq2Y-2QlpBA{&r49>iQ>*^Gkw)g1{51 zL0JjrFsM(lI5&5N*z4im~Cw zo#50>&#+UmKM4rcW1%0>0n;uyxb#?Xbo~;;rnYX7SH>ioZg584 z_qBfDTE#mb6JJZD{NQOpnuU_vLa5!jCTuZyt=FWlYMNBME4tbqE2zQ2GPl_}2u^+} zLO%~+B|=zDHMgC^aplw3^)Obf&n^r(EX_t%C! z4kjErS6mIwPL5h!ksZ97n2`BvpFj;i4IrU%sUC4zo_wCyyZT3mY zOc>393=?u#Z#iaXTO6yu+$yv|y3M@`Bo#(%4VU&06;N1j?v~EWS zFzk;Q1+a8BuCw%u)6+M2lmqgyzr0t_i2&^9ZgGl#Zs%tC@kNB6iPznVie@SP*H&E) z>${K2_j+)BpP%ar6z2b;+P4}_9)6XBz9pK5pN*p#s+L^@AFQZtkLH$RpxyUXhLFDUHYNmh+6X|SnwM-^&F^mGL#wa-WYejJezEC7+v}X* z^yLEt0u!9y3HkIXpsnpOq#8IU5jX?-^($RcM-N73jR%wlaBzU9J6tcC47HwgI#A2k z@4pD-GQI%;^&sa40D_?Sd0NG5rEsV3?Oa6Q2qidnHMz{`j9llsb^CUhooV^40rk8> zm*h$T%$I3U^l&uIbAckX;Rb?R1jqvnU;h+*3sY8mn@#WclMzlKCA+%-q<0nRN+CRb zsOk;H%#r&|V>^#ZoZ&82-!B2{ckc=zMAZHJdH7d()`iLR0Q z<w08>XJvA(1k+?~#8ecP#lfBP7)s|CG#H3`9ZDY9B4+OZ&fk09>)}TEd$A?;Q zP=*Bn)C`WuVN*Ap&qT^U|H-58VF6~R;D_ER`1F6np9|b02gUs$-E{Rt(U6#aFWKJy z>{K9!+{%y6w%#>-Ygso;n%6gupXr z8o0hTh_2xK3TP*%r{H5H9%vX?Q%!GaxboX~YR66E*1qFqI?$}cM+;M9@l5|#)Pg1h z7_6F7>2eDVY>aMybg0YD1{)*CY+G4U-pRteheDI-rLB-}wk1@tcWEEXP*xtyP( z`h13li~#9R@8lx)*F^!l2)Nha;vwbqXF0H@DgoD+!!}#_EJeJWd-s$p^@u`TaKxRD ziJHOrLqIUp`QVks*H}x)ULGkl&Nvw>))GBDK2A`J;w*k=ftI0?F9h12U7yXgeX;S^ zkyuSto}$P~H{UpL0cY05P~*`Zb`)#Yvl9xI_KS$OhxPi%Gzf#=n%~prI3cZlu|f5R zXE%>plCLTScPmeJBz5r<6(k4_0j+qEs0N&6&e3VT=8Fw@NSn zIQO;ya*Z8y)K~yC3iLzY$s&lm-WKg24UA!7Z>1Z^Ee;|WzwK15=5r2NA1KB&iU)c6 zPJPMm0DM640eN|mDHQ^9wow3QPNTM%HdcBiFP+O}Llyk1qo(Wl{-3t+yUgYMG$Z4e z$|Y{i_58nRR18pW33zEp1oKq_L|t7S8bnJAXuCea&1%7G9@*L1fo6w?FC}Oh_M<3t zv-J@Uozm=VX^!=kT$dA<{SLnJH~MDhP(ziai}^xan8G22*h!khSJB+UMbK>CJ_squ zrT0_%Tzh@fs_v$rA+Q(Py9D?0XvCFKtp;vFlPFwN|3Sv{mRmAAX;HTOzP=4>3JhsB7GM%eJ zqD5cJJKwwONqE>fRzfH;<%L5GQ7d_zUzv3k^guj_#qpI?1n0rG&wQEUVXQmFpJBFk zcEZ?hRp)19z#(k{-(#ryfw{YGj1(|{QGgZhMa+x=^&`X;oMZ-90>^#$V{vL$rY2i$DsXDEO0z>5d=7^(J$38l2s;!5c>aI`y4N?~4yY=F_^(Z~7 zo3*PTJWOJ^4p<@&S?=Xx{_zs`!8vykY(o&M-n@Ae)*{qE`5gemaTGxKiK0e+2*?RK93-I|5&;nF1UokUmcVw&~bDg7KLv3LM67XlS@o)TwBmtyzP9cyu%!sT(812)*8U z;>H}!Q!_XS)fNlPU)Dr6xf~>duMI4Xl&~8c_!FLX4u_vkuAy0~@a-MW&YL7a9c~y! z^{`(F#Q+;$=xJWvH2cmxTfhJAeZwI_aD=?ar1&;BIBb27kDtFA$PAN-F{-UKbXM9@ zng!hz-$i+Vwd%fi_abQDeRJE~S1e&}K?C2sQx)OXoHKjG{q@a|Brat16#FB4@|{+< zT5D&hd=lfk03=7Rk&kH%xK|Kj)PNeZ~*y*RkFFW-rIXgt>nHiW%igqWr8+Se~1dPOAkAZx#Jmf@$AI5B0<|q z;oA&{BEE#=|Dx?JsdNe=Al+R8 z(wzd*-Q97=(jB`0=X^N#-m`!EgKM$YyWV%sF~@x7Gsbv^9-iQrhdFOV#G~63@jSEz zxX5d-+-JWmXmIvGt1S2xKNtBFHjRofrSa+>-NmzDVBKp2!ssa%ANs8;p~LHddtu(Y zj}*_j!B6Ph8r|^dIrq+Q{nB}O-9Ykg+@V2LAP#~1oQO0Mor3Mm`+D2+zpg2ALYgCj z3xgBpSx+PQP^xN)xdIRQQFAn{1)fg|zyE21dE^y2j99Td^De2}4fGH0<{r3ska&7| zD3h#<@zda^l?8gTYxwTbksM;AL*-icE`0lTmYczT+Y$Y+@;haC6F+k zdkgV7n&$9=gSC9;E$D2|9oKzhS&&XpNHELb``s(Lx??@rS}%q^WAPqt;d>SSAU#yN z_3IwI{nlyDjr)b5Qg}>dmS=6-%gK3G{Wxa4e=z4{Jv4LsvHHtBpTUvCy5Qsm3&rma z4M8wUEjZ*DckjZYom?|^m-gY%E%&s0`xYDrHO4i~wltNXeKTg}J@aN={-nD8p@M~W zy0GVcEsv>CPg5>l+}n-f3cBm%yhn;-IXAXuJ!CjaS4~h)Gyk2|rnWW@(g1r`LsN$` zu?aiQ4i)gw*e#`I#_&HGE~jOPe(ObM;_Y-86WB6ZxO#nSp|{8A&;yr{dBXYlNIvP? z4fcZbj*i2fc`0PP$9b7GhsvV6y=^<(fu%p}5z&dRGpo~} z+vFkk_KGA<1~Jq!MH5ndr6N5AQ=^|18e^j;FRpQ*$VyvH@n4S)D4vb$M0qIoup~?mXeoT(3IdtI< zm5v^po_-xy#h>=1B(G^Pz|`fL?ggTr%*@z=JV%p3Hq7g+=rJ)dFYrnI640DkRSFXe zl)qGR-Xy;K?Abt};kEc1l(JFp8NTS_7TjVwpZAnhlyC*-mb11P@Z~HN~hoLUFUOoY}Fsd#l=YG`P~)I_oMNPNXubYQrW54J5~gz)H3TzbaFD*VIVcE0hvwPM#nsKMCyjG|T{x`f zFajth|Fty`H3=qjF&VZ!k8>Z_1NmN8S2*WOP0kW)tYG@?bTwLR1ffYz#nI8V&W}Rm zIk`z6^DJo?HJs0U{K%`EeFM9=xErL~v|gSbV+DTbX&*PX(biZt%EXr+3XSH$@*&u+ zne;C#Sln#r?>Nm`AUv&MPOYC{yoDy@nHO}Ff2}~>pC~qYQ{G|r>&Rhq56b9>`JJQi zq`=S^Pn=OFv;ked!O( zq}fB5R6S5!PU$ieCQfs{p8XWV`?|LOb~m4i{=Qj)sMcgeFKuIMb=RlYFjXc> z*e&^Wa>pI|*;?|KJa9#J$rRRV6a1eK$jhjfT`_p!c;9+;fYB}um>ayy%n!cqt?${M zIM6mV)owiSxuZYKDHbV$)*9W4B5A;R6@U6t^{|xoxSj8_#sy=W=OD zV~uCy5^FmdCuoTwF`^ZoX`;^eD-))xt{fk7)Mq@EiO=gPk~bVFBW;V0bVW|Kg)Tz1 za3^$$UI;QSG`HJg_$seKZk0z@mw658-A`AKj_%`~B)k-p5Yc(}L(;g?j8hdnv9#l+GOM$2M0&4`OBHwi+~?@PNVN| zxr}B!eKSAQKW8Q*BQuwisWu!6yGCy;$WS)$zj*kjake;*oJFG0B7DnDWEkuU|t!cFWbY(urt!h(iSCG{~68dWkE~ zbQB83%3fiUoHbh-u%H>TTNyql{NQ~2r>U#QS`2FJcT`ShT2GHx>74S5Co>RwPSIgQ z6;=F9xjG+6n>}zLE0p}`c#()_)@H#lnfnno-T3ZktZ1pV?<3_syum#EH{D6~3$wn& z{iI|rN9Ztn6AAtYnuB?0l6{x)yU*UbC6uO`iF+14OI>S+-^_!osYqLtH$EM&~j#Vm)_9SAAs@2I=gvr<7G=6WK4x zI!hTzw^>r%#1YZf)}o@Z7ByM1G#XYOaZ^k*TI@R+$a;3?5{AZCxO`A^u?KD#>#a+6 zc1%PJyTca7%r+)CSFV(K%*Ghrw#!KS-mYYYplh!7Eqijz|LEjm&bZEPQc}_&)RfZF z(s=s}jpBE(Hxd$&`8!0gu@LdA=3>t^neO~3(q$X;@RDGRPBF6c6`@oKrRKWRPav`V z_=!^9ZfYj(WddDG60C`IyQdsb6AGfjRR z(EjDJ_9oNUTUf>kn9Q!ja;-R6xY(#=wOFEZ*>G1_YeiC(K(>KwCsozLtQ)Y&*tZ*UWJd zOOaKxum>isl6jG>#p&4U_;iCf==OW}PyT#gYqPba+9G$pVu)~1C8a&3+`sS%=+$!i z>7=9tvjh?E3k#nIGxO5e4*OnmQ*jcy#PXoRd~MCDcs03EsH+pbBkUBpJ7#8CSzi(bn9i+7sCS> zTHo@7OfvR!F~t3K1kn>@4)=+^%rIA6(ptYKj(xJHg@A+RV&=u5`rO>4>Y*W$`FZ({ ztdB`2o@tjD)uTLSS{KEvv)D|{Yvk2QNo^W=FP1e=`FzeX_q0+kWxvj2&+1ls_;hWf z3mCLOg2MWjgEfLnP3c8?L!)yd?tNrbt%cMfMSq+;$ zC&^R~7xUcFet4VJa7ItTzj%6h*gz=h_G&=>;PoK8z9qdxPC;zEZ~meJA{S;Vw+U!u zqFz|PU?S8VEg;ew3pV{MPS-Rel8xn6faFGE z>X|S0J=Q1Jw+qr?_ZEPaQ^a@U>(bc36SbCJmy;eb53l^bW>J6uu)-*}w{AT!M_wn; zcu1UalzHkaSyE^6Godt%{qx>Acg@C1%U|FTc`x50-q!i%OUh6@TeYT+>CotgoJ;$z zA~Rd7ZxTq9046{}hnAQ)&2gBaLFWJFji*ScId_gmdQsYo_LwAa&zhDhfyKOOuVBnN zDx9>Ac7_NO?4CV4vv<5ZOYMIZ+)Tb9TJaMN290lswgYb2yKnNhhE=#jR8&`_ov;+q zn%qFX*ITgA-qP&m2OXzf5pAeGG_5>uYHC_rUpHU* zc(iAX@o?cPyZ{p$n%lT0v2CPUNPy&Y0>(5Q0XY+(1q7#qMbFAa3o9=kZtdbJtMYOz z6M25CuNfL5Mxvti8m|^?zt;S^C&!|=3_?WKRdTX3RW=TtT--!} zpoiF3$@<6&R;#8flaXTY!N*jbY3w~P zcc()U;YYNGd<6&BbExzbQ~V$D%Xplg3Hp;K{0sIEJRFbT9VRC4f6T_T`-^fa3_me#X>n ztpA56v3j;rT2_O1Ve_+IaYP{ZIqPNeJbkT`i2Aqq2?6g}NLDKj@8IEa)Be54%DEv- zii6;~i>IQ}eaApSt!`T~tb;VOtn2!N2Ye*zdVl8vu-tKWbV!hq`mmyx%8ea79Y^PW z*P&M)|7Bu95%*3JMe*az&p{5~5xtow`?Izlua8%a1OQGzQhJArim1+VLsNvO^)itd zWBHw_%u1R#SxOlw@I{9m^ZDt?vlm^p*VGE`FPJ-7S*0{);#&;eM4q!ge4uD(ce$)p zFG9x~qGI0$zG>o@ZxX@AtW2L<%#7uil*DK}igl*rdvM&Zfluu}Y9YguAk+6{lcv2P z%v-=+_3O~kkGZYXXDW%);L$6aQjLd1z*VNZ2t^FV+>AkYPf8Nuiw3c=#dv&1#TPYwQ8xtii2HiV& z%RNx@;jK9>nqPhJ;KTyw#DK!pflNH=H7mJXyOaF}gMI<75q-D9L~muQi{7>7_mH8r)lzo&`cc#AC_dnpPbVKrS9Znnk$ zo|EhY>V){3)x=L`Ny#E9hU?{w@l8K>AXnb70ITDnj0NP)G?KhAqd{^~slZspBapOA z+qvmD&oR3GwPNvmMvcL@nR*&ZzMvvM&AFM~7}!ub*i&Cm0<%O_Su7u2VqEN$Z*UP# z0h)Vj>;#pALqxFk`3wvYtgV}gc7Jw8u2=SyLK@A~Y(Xh5ni8T9?nJLrF~2-bBq^XG zk2CR2O9fzxF6+la7KwO!WE%v_!(wKu3SY*9%W()!INF!h`3A5>ldr&=`fc^YP+z}%t4m>t~X{KZT}JUy*;ujwqd zKrbs}MZdK1Z8jF?QJyhSRmwS3kTbY4H^Ot}ai+9Vk6#mj3z9w~8If-aU~)+EqQX{QAl%!6~RJ=k8S zrHg>1wL=MK6%}+h4-af`EwZPZ<1>AuCHZ{F)eEPgEKB`&Q+W}I#_uUTs&d;!#tydi zZBLI!2-&`*$6G7(IVUb>={nFzzx(P%wj-cE;-g%`dI6s%?{QvKkQF0r1N|AnHcjtu z+SIO3CdfWHx=X;c;(tx!ol=j9M6=3aqbHk{ioFyhLN-kp*P%O-d6c(10 zRrx_FGVBky_n|cDyM~5uaIy!%)OnG5UBf0-A{TUuJNj~<_x{fRw4JA)z+!fXU!YpT zcBQyLiR4=Jc-Wi)%o+cmXj*(mg-8Jn&4D1gDX6?l zZ|v_?|CIKY-Fd)uvE+?~y~F0>efmW*_wTO+S?FH7aMoUs(u5JR-dMI}N74VGA)<&PEUf=s4Q0d@6RKDADZ{H4iU_pWw z*@RF1D=LHRT!QG5QG~C-xS?~3S71Pgfb2c`^aNP6c042Ffop0D+aeJ9Fi)Q&qWFC* zYI@ca09L^r$0t`R*tg_cVrDB;22HAg?{qn4JL5}a_q;ViprP5}^5HIy(0iJSft+bs z7#n-WR64YfZ)71Mk#|1wz^={W?X9bMw6~+=ixu_PVu!+-8c;W(EGq8p!->B=qd4p# zU+GLj|776t#)XC^y2{BBIPL`o`mQ%KogUJhHN4PE%#JAnb;7uK6Yl$)4q{EJKS$*^ zMAXn0^EsI$r0<#3O|90QPl-|jfX-~~^g9Yyd4kBovwyk-X+3Spx;O!b@#ST|on>O= zR!WOp4KcPiFk@nf>V~v`+fyXVZ$8Bm#bJHB30o`C=YvKhdEA$NyxkwHw!jojYQaDv zMBkgK?Wz{7)qiqa(~%;7=RObHi_Uo75GMcpFD4T0I*QX(U3)cM=KYy6LY2;~m*MuU z7AMk)h&gMRJA300!_~?T#XbzSC;Ja0O=WbNRaBD_lHU$#WA!z-1yT9Dm?AkQ;oB0c zx=_d%RK4d`G(XUnW$Mdm!T(TkEIUPa@{QI<%6>I`vz?y+pvc`!xPGhYcC>K?PcVa? zezadajmdj9m~Fa+;-|`Qx;I0fIjo^j`A9z`=-exqM89;_Ox4!t%lAjv2>)C%{HIt= z)g+Qqi(yODXtg^7^4OSxQBvR#BFhZEm7{qmqxws+5kmk@?k9MXmB6M;=8Vi*{kV>O z9E~dY<*dJy2d*9j+R(F!@~5kCMNb6pQYgCp3%`6R0a5AwukqLuX$S`{Xd0ksFYu&L zeO{ofReZVS5R5CBmPn_yPILuE*zQQ^ceG!6k@LLH~EcJa}+TR)>4*8!D zB*YaNFaPhNjLP+`d6?QuCvR~}%~*K47y7BET>~M)StLx0wgBByv!p-E>AP_qQ;oGTOB300z9-{D1S!6;>`>!G|ErA!1bN{IE2@;C)GIuHzF) zOkjAa?2LB(mI(=oG3N5-dN(PSPIdjYPfkpC;&y9zS_@>*-DGQGd)xUeCeB^hTE!tF zGi5a#yn?}Z&@p~k8v;baL1gs3&P*Ayt>tdw(WPPi-0er$Z_48kcM1Tr{~U-}=?|zi z^RM1v9*r)2x82CDM^qRi57ENPgyQDSqi7H#y8I)XvMrYT;#?l*R?Y5QU!sq8_zFbg zA$S;>CgZ?^L6Q(35F3L%mj}1+4g6G^Y60f!@avpbON4_3`tI$#$F<|eh4mH79Thx& zWV{}eUN|3x4tLnqaQ-{ME0$cY8BcH>ot)q{I`r4SrS;!|Xa&SZw8zi3gtJN9Xi|FZ!xLV zYW}%WeA;Vs{v|MuY@NS@*K7jVfE>%lvz4x+~h+kiDSF zYU<~scYq=8aE@)~$ z_O3o2ul?%N<1Me%3in&@X}(Wwi*u3hyfUvExcgUVKQ`h98k1z0E2PpGsaIkX`HJwF z8VH_szIVVPj!i6qto=g}9n)n-e=0F) zyvAmv^n%-^*T_SE|IPP(o<0QDA*ky1A>Xj0;^{rGDhx2zpTqPleYNJZl_3O4eW!g0 zD5^#^It{g2k^{p+!_c4ZSiQT>Cbx0)_=%hXv(r+NJ9}wfGMzbjB~ESYG9#qyjqT5R z#l?-eN?iyMM33y*-0D8gHXX%FsF#Tva2*&}v0X@Uekf+wK9xhvUgn+@yJDKtdKHuO zD67Izy3JWK`T#S6RsUUy6%Vj@hDO7C88vA0JMlSA!d#k0*MB8aB+EzzqSnatVWHVc ztyT~xp5KRnd5eTg!amO~*l2s z-=I=!IzBhUEB9B(T%-b%n$z~<6XLkrr>wkLW?B!ng_7#pS|i71W^|RX+oCwnP@HQ! zsl=z5T^-8DV0Jnr;#rxWzX)UEa~QAE{rMpGJj5R%AD_867VLn+gCEROcL=Cy3XaXq zmOe@iMhFom3}LMJb6x zg3Ff~wwkYALGTY1FswJ;WPud6!VUwR@kmnw3f4U4zKmo4npiX#&85FLpPnmRcQ}^E z$0@G2K(qz5WW&uc!4vCaa|QP41*`yzWjdq;4-mQAi@ie6KF`ZE++L_qRCwcSsFWaO zTQt>mU1HHg8Uw;F#hCTO))*8oDVeTy1mpN&@vRpqxYX%9aHu@E1QB?V(5vANZFIC? z^|0QhN$%;gGTl#OlRiC2onnTOq{>vi^}WDbBCzQa#=Eu)gatpkoO3@fZQl+>25f~U zC(UGXbw#$r)Y@8Gv0zkDq6s)Q%zbAR(Ut3XUs_n?HZCf@MKqwf&0Kh`qB5GG&O_Fy zWm?7z>WSO9&>b$EMRa_oHQ~UnuFebE!JydM^*)GX-<_C^?GJn6g?m;aq(vk_1gj&? zjZEEkTU`yTGI$rT9UsYQoSr<*){_Zpv>qw3!aP2( zs&0!WTO^Z^M0nY4;=OBv{BpgyuEccblC-63P}fZW{X&w}`ay<=#}(sRMNpnqop&*u z*BU}7cWF#5jX9NZMcQ;Ow&fyW~tK;)%OKRNvpDI7CCe#&U) zbs%YtIG+I7o+8boWp-EaY3_Sg@w<3I`T+~NcwhcsahOJ}kiPF!tO}j3PtiY~DjXgJHv>8UCHVxIl~SBp znQ^M&ER<7htq`!aE()$Kt2A$dVzH0HYo8(&%b#CY9)^gox z^8006(lNu3cIY0)<3Rse+DT;H-emOTs%bgfd9^}kSE2V<7lU4!Fv!7Q;?XcSj=qc1K+<0@eF5dG2P} z;R@^h-C=SuH7_im2jH{Pl$oztEfj&;mRm#f;pIs7RxgoBrNo@&p+aC$Tv2e z!S&NX`5M36iKxPqQYUwJs9FQNY}q_TK>8b|E7}a>Aw8U%cOm(Ok{Oj z-_daleS5l-uH;j~27Y6xAoxjzR>NyP=>*vtPvy!Dj4L`i6UZ0=73-BDdG>|ocf>rZ z=Iw6d<%ByysC*h4N9JoN=G__3wi+&bFNU+4Ak?GR@!;m~m@O$cmifuba&C+sAIwvp z{xHoo)ya8{n3_I*GwLE!z4Tp++I>bkdXSuBw=yWl^DHju$nc8_F0Q?U#q$UA&euU>de#Zs?BNy-9Dp4Y1O80veYOrbDfeZzix@i;1xc zh&|OxY<~)~s;#?nIEupU#1z4f6_mGqv>#pMRC?6nje8b)tKK~!F^HR!9 z-<6c)xcC-GiHx_fWQF8OGgBV|r(l-Vidqzj@q zIzkM##r?=mR&{4*S;K6s>JrjR%G_T^kKn*z{vuIv?77LqvpDw6H+H-G`*%J#g8b{) zE}D;dhm|gQCD*{%+$hl!J>Ehk;T~H%A$!*}FUK@~^cAbfjP0q}k6s=NAU>p_wEFT* zX`?%Ivy+qOqgS%N+n1H|$SQr7geb}R6qxq=Jd3pI?aM>cp;r@=R_h8!ojn~^E;5&- zH4M8B26QG&u7B6djEuT=-HuF7zLM3xEN%=swYjMAEiW!oU+)P0fDnN7+nO=jmB*$N z$|zM^zRO-cQI|Q{@lmnp<@jwMD@7W#*RRoTp}qU^_rCiGdkJ;AYCW&?)EU9EM(Ibo zcUd+tP`6oUh3kzJFVFgD(LD4iIZ2a0Ur?0xFqOvx+DBe@GS-77P-MGqn7i%a#ldmd zo3!D!=+Ebz5nBOoo-UUB5mV(FghL@Srs{Kb8)RsnL9i4<#5e4vu%Pq)w4@mq4oD$*!+78jiC7m!<;5 zCjL4;YLY>;M6Z9xc&>Z#+1C7aR;MX%gV<;>8S%=(t0~pa0a8347Sr@Q4(!!R98?-5 zc^FcW7sjD6=Rti?ka|t!>G9c1m$1oMEXYP2Sp@Drin(WVgWbgO8uyQ9X&R^OE0v>1 zSdE>X#sLur)bPPEjZ-4un=!V+o%ZJDJCrn^Bd0bJi8-7_kUO|PUjePb(5TC*Ab=jT z^uUx~!wu7#ia#Ne3UF)f}vCeDN{)al_ZyH673f2%0_YNu6~+*mhcCU zzt5Osj^<-?%QtI7qW0To2Ogz9L1(wOtdFs-$(meo+v8p`i*mA|Zb#sjLb~@qI3K9+w9yAYy^Z%hFUYPBaD-O){ohLT135ue=qX>D{e;#r_ zpqeMWFPyE0{`YSxG*pM< z%uqUM+@)ZjY(d+E#oh|f^f%}8oa8EyEhc^sc1cX%$p1H%^ z+PWo~suB8c@^~_g1@80P12&x4P_zLr{w8qHv#&1QLjXzhJ1gWdaA}9c&;4g2{69Y} zXmtJ%yB-!WTI2F8d@|uL_-T2L@an-6TY9KyEX!n~Tyh+y_9q0T{f_tq}*6K^d z|Et?f2tf=NMfh!EaPWuPH;xk*$M`a}#{kZM zKSqJZk@0yF+Ph@sKhvP<_ZAWVL3#yMa0Lat)9-_jorutOav(e;jc~<^$at91dp+{l z{@LwzLR|BDey%^BLk42CYxLC@q#gse3I7`z_V_=CB>4-|ReuajZ?OA^5}bwCRRJHk zf2?b&SknKU3sCQ(G&Yq+1>cQ|3O+ZN7d6A0^wf4xK|>Q;bk_;O3jfZSmJbVBWn}@1 z8Y*cu@TUi;r+#sSM%%ODP*Wg_`rCZhhwLJ>SN1QCZWDT8nLG`Al8O)3EF}O85Y& zelwYICMtZvrHj-2B)?C3{*)`Ox!zZ2!9U&ws3{y>(#<~}16k{_bn)<_)$ap@q=g;+uResR z)Srx|>asOD#h3j5A2`iXC57Gnn;r@{zYUqFG1r|a#)gKf)7BW@n`90xY9Xr(1SC&XMyqHSgs78neCAA8Sq2(;L!eYBAjyAaksCX)6gLE;=}p>&Q=0l zJ~M{ZxWND0PXNC^g(@PC+VHgnlQmgGgD?7j6bHnH0y!ac0qYeUgdiJqa?XuRs*k~= zD(4f`-kxoOXrW2N-eG+FfsaKFa!{N~ttlY)AI>|F$_u9}C<-u3O~wTd_a(58@0X={ zoGy_J7K%dA-AT3*gIWZ=i<;V}Q$8f(udA(sOny`?tw4+AFQ24t6EKN@kPH-cXY=S# zLYBH8)F!-OB{KRE@Fekn!8v}4sl(uX;4{%I)F7G6PD)YQpDEKu;%>NK=T6)&#n^7iVmd5{`Gg8$`TEzdkPK1# z_ok~ozH#F-kdvfj?JhVERKk%pt2DmEH}$)d$J^t{DN%13_Q%2Sg|QeCTw-C#vODH} z!5TpqjAiIV#-6U`GZAHkBpJ_~oCMjcL&spagh;dRbvC?BJba*&{FoC&!d{ z*26Zrdi6QfFF@e}W3lB0Zif5!?^B%}Us=hBP3n9>6*?aFrF7zy*sj<$!||oj-rK^& zgWHU^2M>E6Lj@8G!0et9%v=8l3>dXq--mMN>AN%3G|%DqxT4?Jl1ysR3_YO7b)89} zf=NW3aoRQPwRs%&Z(FfHrRhWUy-qK}`!D0BvLQwAI8I*reaE8*sMZu7C-TdG<>|&B zjD~F!%~YI}oVe^`HO>sm3Ut+zJ)0QFrkRy;7(bst93G0F&>!_`4X|$9t3N|(cZ>Ks~Ga3`S@`lCP!97cX+Nd$N?L3wJs|Z@n^d0qbKd3 z%qG_s-i1B_`W=!BAvwwH&hNvT06BF~Oondg6nmonYAN+GF4;9S$kI%ZutX9c|DNv= ze2xvsTQ57^QCr*D5pjGHby9eNzSnd?w8Pp+T;PeMWa2XLHOSmcOPL4fuj*hs7G8@^ zNEn}=f9s%@R|O?T)K+k;_Wo~r6)4# z_CyDK8_4zo+0VA=U`NIVWDAv$zcx(S%@#M+mvQ3g#ohni2!9nk{toqc#{c7ii@-Sv z8esotYfZ^O0K7TA>3&m>y0>B83CAyYXj2qD8}mPy!NNT5il}Mj1SBvllTS%|tn&Tg z#s{=9!AU#&hqD;IXkJx)k)pO2aO^*@b>-a896AqN z>fdpz%`3!O3t*G~eiQ)H<6m{6zeXoJ|4%TQ>Z%9Rj&M9pfz-NxA=7QHhCv5Hub7C} z3|I%`7VQFFh0V#nY?}Abi}A+~Wqt=bu?r3VWUG9Cjvi!aB`6-f!X}LwH+eJEgA=dG z4f5*#1=xy#?rG&->)r1&De5)_j4<9+dk#iteNWQ!1t1Htb42nVH{y#>h@u{tI7Mf7 z*cHxsV||Hv48S5B1O5bS)aEaZ`(@x*MkBU9=gD`HdlqJjLPdBHZGufd0uCaRV}IT? znPD5^i0~d3@sCpRDNUErYgy^fMv0tepx*ik$!~ZgiEbAW<)lY#J^(HfOSV0 zJAoVuZBc)plrNjc56+*iE}gc~^mu=x$wWd?+zEi?|5nMW9k*3~w^4dNDuiz^1vpgj zP3o^w-CswP;fs|4k}wnhg^_BIa_K?%wL<9kF-Ml%0t{XK!(YQu4L$qj(!Ku`D8&B5 z4u63{;-KDNC^$?0XWgnJ2)6k^C#uMvr_sn3&>+1LRBsI}q}@=_&Z5|FFT6fJ+?AMF z9jzoQci0Uva*vWt5P@tuE(@g2q^rL_P6V5wC-jh3GK{?K6Jbv7@zPdL9UqEk|EWqI zxV3S{5n=-;tEVS8GQ9hr+zaMhxLi)|gIM4rIk~eaMOKRhAVgtya%6{61v1JV>1rH} zcYl=H)WZwOzNB7*&TK7FkYz?i>FVmj1x0SqWc~>H%Sfed5TXL&D)xK2)E|}49}xq$ zKJ)%yqNQ~O$pqAbZ=9xO>lZ+n2Mt9TTDw61Ey{4RYuCJ>4HX{B8Yq}7Lh)%+no2IR z_*6b=V)XQ+@KX!?{#q3(&Va$D?|*8Z-F$C^o{=|y1mb8Hu!!c8Ha9n!E$1FViTN|o zjt0T#j`n6yTo0fS8UuF+O1xuGlTNcpAgK90M+ey)$)A)bAP~wvPbMaw3kwTtf_gSm ztJ};hTU$*ADuO|60yuOG&>g4+i6O~?du(i1L1lnnMI{_CTe@mEz02W_@$o5$MBuQ+iRHpMfGX$CUwA&&X z*OSwtS$mJ8RHUV0f{MyRNy%CeOWF2E7T{MN?+}YiNMu{i%e90t3-OtPuG>J78KtA=^E@r;!SR=x?$tQnFiI{1Pxv{#+I_abPTw9HyQk3Cwhn z!S)yb2g)ni$T_ui{hzU6KFGhgMkC1&L~5rwNCniE)e1w=9Wr42!QBOE<-Z-%s-y!) z(-vVT1NBw$3$0R-><{$yZ=sNb&U$MkM}Ip8HnyiyDw|s2_$e$i)JG@!j+&Zr@dsy+ z+KLd}W4Vn6`pIs%XQ9m$8x;D1MZA5>Y(L!Hjn5t3Hg)%^eqUNR65|P+5fl=l{)O>W zF9F8O&86O)){(et@klj5Lw#rAs#B7l3AqakpUho&0H{5Vc=AthHt!*ilehUA4zOQu zc%F@Z`~|AqqUD%f%V8n1n+(4JbA8AdeFg;w2j?w+QEBPotUfDIqYmfig^ayZ(DXrR zLhJI3{i61zav~=l+yX4*uX8y!ST&QLuG^55PrY;St?T3Qpj9f+nC9a)84vOtrzR$3F;Mdv(!{nV~d%UB4T0}A)67YZW{SBa^-Ojl=n12%q{G` zoq6ImXk(GW7y<@lWJId_$jQrJg}@cZHBe#0en4St;yqg)4sj4-C^b=3cds`6)WMhXK}V68b~f zqO{3v#el}y{=n)}&$65LW#8af3ZRStyBEYA#tx(OL+W^u@1-Qt$#c{@YMe2 zQ_p2=g>rt*hS3eQe(Ph8EA431a*qT6W$jItvxRl05d$na!Dquod1>Az_mhXqRk+KO zpu&Mpu{Axt$+nHagAr60I5o@5B7whpk_onDS^*G^1F78%)mU3$a z6R&b4)UYEV$bG4iWg ziG?4e1-TPtm3!a{zPrWHGq{i5%BcoBixSH;Ke`)i6euWw3~KbHAM-|sp??T@F8%yQ zl46*%qzCOFpa0tcqU>@c(HU)cpgl1`l+VM+vphG+k!09o2|YGElHJT7sc?BRONM8h zqh+9`d)^!V>&Iq+h35V+FTUjHp&P{zaskS+Ww8mpynYF*{5uwZB>?>-!(|3+euCn9 z{V9I*k4fAqX?{4odnnL%?p0~&J!lho2igOC`uthRFxt-#eNzoaD>176K6C+fg@x=CEwtx2d?ehM=$8n;RD_oodQyl4h=H< z17+pU_6%H*M~3r0!?%BKa*d6(b=aex_s#%H_U~@0<`{K!2Su?eNHzoEzPZs#K9Ka+ zgzRvch?`Oo=Rl^>a(PhEMK?}9Ng7vB6FebJVLEKWTHoFYfY47zvMeR^4B$`g6<=8H zO2h!M8)O#|h)EK+-`~Up4MZ>KDvM@Hs!> zE7xws10}Arg6QYYol|~V@U+x=06M+EMd91fA%QkU3*h12s>|u^@N2}x7!>EA|3Q+e zmX5jR{61^`yOeD@JLjol^Jx%~C^}pzJ%a)rHvlk%GHGPwEqk7Z&Wqy`kdaomeMdRb+vmI-2xW9h}1@bcCfuX^P z6g>J$ZmN?tpbN6#!>eN1SFx)JTAUdd$f-Fj&(RVr7Ut@oi`Q!ELj+t5c|BQPz zs6Va!uo-=}G1Z8E@z!ZzB1naceb33cd*#}-NQ5K-+*DGx0Qwmh7Z()%dOs2hkNswE zx;A>aTja?A2Kd5-h+$spSMi^Wys$~gh4;g;6Ri}Cl9W;v>Cr-BWF!u&d+dYn^LeHe zZKVL<0xN}P8s0-Dv}gy`vv5NSu761xxdztjPsbVdXMkQ8$UpXIc*~pVi$m)yVBScFXShjcd6||9%!rlWH zga_Ow3W%Eoj^YHSm({b+rYfbyAqF6W5Kf3|p4vYTew^uuCm^c~J2JHNAw_0oz#2jD zIufE}nN5m#czWI-Cr36lVCD)#u;^mK#CIl2Tmx}HS&dZ?!^6FP{UQLso=gqo$?qHB zm@_D#2TaIgpLgOp1ZSl9qN2UiLYtjz**-LcizT90ZqQi2*4{XopPao+^!80F3M1JO zx&d4(LUqGOsst}nE;a^z_jp0TK_$f+a`mf3i1Lf;c-Ll{`P4){`!M33MPE3VuR3VX zz>v@LV$#Ot3fT&2TNnH8A&*V{?0ZMN7>AhBLo-q5_RaC)S;mz;ysK!L|fe2ZFxhxX=$+zj?TJhU-toku6; zL}mfXZ&8I=Z&`SeyhU9LGB@KV=f7`4fX0049He$;rce1lVAA# zU7LYGP86ovGoQ}ax;g#);5DkDKi{hD3H{?E|EI56Z~c!;{(Y~42!2(5#6alpx8n8; z`aC6oio4;`KsFsceGj10mE8b5k*!Qc(bKcpkBMu2+%cr2n46Ed&7V_a<92>(A+e^y zuEH?W41m1R>DgIBIHr_1?&nBHb0;EOR}~cWt$w6FXNuSpE8~QwW7|$$QhL9QiXehe z8#+kY&nJdHH#vkxR9kSs-eZlg4O%j*3O}3sbJQ6h7Eqd@sJ}sV2W>tTXkk=2z5`0{ zBgYn;h(54QnB?@XF1h@|!qP=Vedo%L(qQN`1-h&xypGDNWt)B=Lw*JQoF(AY%f^S} zUSxDvF6qPZv#Ghc8;Q(G^}uCBK$~T(T|+~|ZfIl`3|EYXmZJBdg!V}?AXAwjWJ7`A zkdTr^Zf@?ay-EMuEG+V{o%KRY?!=-f@qq*Z6RGWwPDse%-vf~jdH9ECnZmz|QuzD$iw5EZ7ydz+k*Aq|gg)o(nQCw5ah>XC*< z?83r=f}-Np&9d8%GZgj&eF{v6AfLLg2tQ`GMPSQ(+7s)`_ll=KLK*cRKaHk~abVOf z^}Tv^p~6xO4F>@a9HeWz-OVz`{`&RnmG7@IG8nl)n zl4&{?$A-YJPa|tA=zId;L6ofCp%TQ*+1S{`f-3eVd@ehsy@2!1rPqS?Ps1&8C9!e#Z;!3g?{XSHsN52RccdHljUmpF1CFDS^0f~9@?kn_KHE4AU}G!t{OU@($$;6F{n6f z2-S_hWiO$zq2Ub|J}@71VYB7t)7}RTHx?F`efKp^swc=PDTR&$-4?GLPzF+r;$%u7 z$ZveEs6As#fsL61jh%=YADX;)(J@?X@nibv$Ps2iPcI2hu(KJ&OZ-1d??)L+h>OFN zB?7!sgGnUZm^|k$;Iuy-8uTf%;=rI*4;#psJh)QW5XPlTJ#dQK<*nB2@Wo^Dn@mng z5j*C&{Iu+)0CZA;;TjMCz*pf=?@6|bf^xo3Ojtl+CXNLGw(a$juZ+>m^$pGRD{Z9H ze9d{Mq1a;9vj3^gJm^#_fF1jh@_YgHe^{jhKXi`|wwe18Y3k*JPxa`qVI^&?*OR3t zBnW|t5^qc@C@8#u?ao{H#$eVnNTeY@h;YBoqB{ruKvqhcn#4)C%!LCV3U6<3N3a`5 zl&%2O?}bh3>RVw2HMP`7v{yFEU;!<>(C>~_w|h{Tl4g0cjGsJiCbQByGy!L$pKnvL zg$*tBak_Wu(=r`Iz;AxBZ&I_SDgR(J7AIwPs6e1l!izKD6X@z1fVXUFY3YG(n|Thq zD`DHl#>T37F9M-`O9FTW1!LoWjcf8|9RmZ&x8y(5Lfg0A#~LIXlh6xA3>qLjx!f z)7IYpZ2_lz9uwuKK9{3i(L#wrPL$juT(WiNS(H|B1bMtz_`Riv2-j-~W-qCSQ?wM> zxc723zKq=JIf6wBq)LiIw$ zgQS)EQ{XC%3ngLGU%nQ@md@19=vx}26EevVyZ3__wYmQ!X?|+eCd=rhiWBm}-0+B@ z9x+^dQ#cOWN+W_KMG3XO?v*E&71+l|kE6Iy5?;>ydkfbn`w@A^i$y2JFLSZq#iSl5 zkjhPlX}nma&EP_@z(w1EP4Tk4#so?k;b}oUSsTro@dHh~U(XQn0V8M`IiQA>7Hd5% zUfXJVcPlMWM98t1U=9B>+|Cr&yUzaojp7 z>1>`gOA0s)$zK>T?au!N&(79z=wt$MpTbH-u-CxF zQ;!Hj!K^P&xNq0WA6~0a$e^s6Sa=|O$|BBi`<+ftfdl!j?O#Kec^bsI_~-%?;ErD> zML|w5m3^b_-!94yJcGKUV}@Lx1>cXZAd}3Alh;;G=DN7(@Gq^)DiZoi%ccPvBsxW# zYVWrvXGy;YY7t>)P+M8vwe;;(xT}v`nY8opqt`qW5@hzXGaT?!sVvvnW8PsQ$~9{E+O^=rsMA!844I??XecQC+%|N$y5slVT(F`h6d&fPwp=Ka8E%=ZT%6(83#UA zGcd?BsJ-WF{mv$6SP87MS^DlXGdy6pGGF#oz}MgkbU|>)Y;frDn5`P3_G65IJ;Q@(tFf;66 pD+IBik{(KYARfa5DVze6{_$rl;yfxkTPg#j($m$?Wt~$(69D(cJ465g literal 0 HcmV?d00001 diff --git a/docs/images/dg/RecipeClass.png b/docs/images/dg/RecipeClass.png new file mode 100644 index 0000000000000000000000000000000000000000..9decce7eee72aaedb0b561c4fd88fdac23f68f1a GIT binary patch literal 51557 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 SR15pFN^NyLwQ`jQ&;AcY35&=8 literal 0 HcmV?d00001 diff --git a/docs/images/dg/RecipeDeleteCommand.png b/docs/images/dg/RecipeDeleteCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..89961d8e44d2719ff4fb8fdbb10a4913f19ffee2 GIT binary patch literal 17233 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

5s=W^P(`DG4Q?9)`6bk(|` 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 z zPW)qzEf@$bx=nGFErOj`a4Q!&Gd*|e7s zwl3^ca!NUDRPiKJJGR%r~~ZW`pOMn>>)`{VvSbI@fFB z`oc?Il+W$WIde376|70=?jI*8!vyrz@Z?`ntKWb_UVtU1q^;_ddJ!=1Eh7bf)P0Hx z;4iK}E?`H2)6R4fk{j^l&bW5Pd^$^;bd1@B#MJ+n8+`t-! z)=O5IA|1;G( zsPqzqUjW~F0|*$HGw!9%WU;}qq;hF<9aEP5*jxsIC2e1Q=4tcXh^GR1k(A%*YDwx zebPhMlrerl+->WW-p`2|FIfuHEuMtp*jvdA;1PzgQx3btT@})!TDXSY#a@9`Ru9XM zIx)MKahRUJNw5##0(l;&(ZERLRJVmF=3$;47izwfc?bA+jy1wR6p8OR*Ok+uQa9qr z_$_=|cIuJxzf9a=!2fIi0nhLCg>Iwsgq|qs#Ed&-xy`E|Wkq{toZ*o+-%H%Ms|~#U zUQD~74mQDpPXUTc|5bF<q@sYLc_JbfaEkKag#hQ=-;m$`V mCh*DEVR@3eqtF`7b%(Dgi+CsJE%3kjT{cz@r;1Npi2VyEW=zKb literal 0 HcmV?d00001 diff --git a/docs/images/dg/RecipeNewCommand.png b/docs/images/dg/RecipeNewCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b2b60936d005fd97e85fbde69241299982b889 GIT binary patch literal 26967 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/RecipePushCommand.png b/docs/images/dg/RecipePushCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..276ac161bb5cd7f79dd74666e10e931f4f27e5f6 GIT binary patch literal 30417 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/RecipeResetCommand.png b/docs/images/dg/RecipeResetCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..0665512ad951cd0b2ecdbf5d7d6ec5a98f1e9ed0 GIT binary patch literal 14033 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/RecipeSelectCommand.png b/docs/images/dg/RecipeSelectCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..a1c197fd75e689b825a84fba70b809484ada51ca GIT binary patch literal 18131 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/ResetCommand.png b/docs/images/dg/ResetCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..6294819e24d18fe16c8d59ddb94d72d58440ef5e GIT binary patch literal 9779 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 TW&-#a3`-e&M_1{H2 literal 0 HcmV?d00001 diff --git a/docs/images/dg/SelectCommand.png b/docs/images/dg/SelectCommand.png new file mode 100644 index 0000000000000000000000000000000000000000..2ddfcc113704f672e6f17f32cdad47dd06400a65 GIT binary patch literal 33343 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

|fVKt8o8KO-1!u4QXSAsN+p@YPq=*OY8M# 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 OH#KE#rR)>GUj7%@^#5=G literal 0 HcmV?d00001 diff --git a/docs/images/dg/SelectCommandModule.png b/docs/images/dg/SelectCommandModule.png new file mode 100644 index 0000000000000000000000000000000000000000..72442e0f5c4e6a48a202bdce45bb7617d1acc323 GIT binary patch literal 59295 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 XI+s$m$!e|^f-louW;@e&*oXWdy~@Wg literal 0 HcmV?d00001 diff --git a/docs/images/dg/SelectCommandRecipe.png b/docs/images/dg/SelectCommandRecipe.png new file mode 100644 index 0000000000000000000000000000000000000000..9c3ec3bbed103fdf5ec302dd79adb6f07c8172f6 GIT binary patch literal 34368 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 NrKzWpb?WlX{{t0*mVW>M literal 0 HcmV?d00001 diff --git a/docs/images/dg/StorageClass.png b/docs/images/dg/StorageClass.png new file mode 100644 index 0000000000000000000000000000000000000000..a80cf0463ab7ea09d1540d7fd6746abefc7d505b GIT binary patch literal 31418 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 literal 0 HcmV?d00001 diff --git a/docs/images/dg/UiClass.png b/docs/images/dg/UiClass.png new file mode 100644 index 0000000000000000000000000000000000000000..c3dad43cfc9842e1b6668d9eaf057d5a906c7a2c GIT binary patch literal 9482 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 Date: Wed, 27 Oct 2021 01:28:52 +0800 Subject: [PATCH 363/607] Add ParserClass image --- docs/images/dg/ParserClass.png | Bin 0 -> 14084 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/dg/ParserClass.png diff --git a/docs/images/dg/ParserClass.png b/docs/images/dg/ParserClass.png new file mode 100644 index 0000000000000000000000000000000000000000..0d14708a07fc7fbdee125271f96e92bdc093d6c0 GIT binary patch literal 14084 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 mSdk Date: Wed, 27 Oct 2021 01:38:17 +0800 Subject: [PATCH 364/607] Added DG front and back. --- docs/DeveloperGuide.md | 182 ++++++++++++++++++++++++++++------------- 1 file changed, 126 insertions(+), 56 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 9658d0342d..b11794eb32 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,77 +1,106 @@ -# Decodex Developer Guide +# Developer Guide -## Table of Contents +## Introduction + +Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly transform data from one format to another with extreme ease**. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. + +### Purpose of This Guide + +The purpose of this guide is to provide more information on our application, Decodex, such as the overall architecture, implementation and design rationales to developers who wish to contribute and enhance Decodex to it's fullest potential. As of the release of this developer guide, it is written for Decodex V2.0. + +> ℹ This guide may also serve as a start for software testers to find bugs and possibly edge cases within our applications. +> + +### Developer Guide Usage -- [Acknowledgements](#acknowledgements) -- [Setting Up, Getting Started](#setting-up-getting-started) - - [Setting Up](#setting-up-the-on-your-computer) - - Forking and cloning repository - - Editor - - Configure JDK - - Importing project - - Verifying setup - - [Before writing code](#before-writing-code) - - Configure the coding style - - Set up CI - - About our code structure -- [Design](#design) - - [Architecture](#architecture) - - [UI Component](#ui-component) - - [Logic Component](#logic-component) - - [Data Component](#data-component) - - [Module Component](#module-component) - - [Recipe Component](#recipe-component) - - [Storage Component](#storage-component) -- [Documentation, Logging, Testing, Configuration, Dev-Ops](#documentation-logging-testing-configuration-and-dev-ops) -- [Appendix A: Product Scope](#appendix-a-product-scope) -- [Appendix B: User Stories](#appendix-b-user-stories) -- [Appendix C: Non Functional Requirements](#appendix-c-non-functional-requirements) -- [Appendix D: Glossary](#appendix-d-glossary) -- [Appendix E: Instructions for Manual Testing (more details below)](#appendix-e-instructions-for-manual-testing) +This developer guide is made for developers who wish to further understand and/or develop **Decodex**. +This guide includes the setup instructions, design, implementation, testing, product scope, and other sections to give developers a better understanding of the application. +## Table of Contents +| Terminology | Definition | +|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Data transformation | The conversion of one data format to another. | +| Application, Program | Refer to the `Decodex` program. This two terms are used interchangeably in this User Guide. | +| Encoding | Convert a message into a coded form. | +| Decoding | Convert a coded message into an intelligible form | +| Base64, Binary, Hexadecimal | Common types of data encoding standards. | +| Console | This refers to your command prompt window. | +| Argument | The additional information you provide to the program's command. | +| Module | A self-contained set of instructions to process your data into another form. | +| Recipe | Acts as a container for you to select your modules. When multiple modules are selected, this forms a "module chain". By default, you do not have any recipes. | ## Acknowledgements 1. SE-EDU 1. [AB3 Developer Guide Format](https://se-education.org/addressbook-level3/DeveloperGuide.html) - 2. [Setting up and getting started page and related links](https://se-education.org/addressbook-level3/SettingUp.html) - 3. [AB2 Code Structure](https://github.com/se-edu/addressbook-level2) - -## Setting Up, Getting Started - -### Setting up the on your computer + 2. [AB3 User Guide Format](https://se-education.org/addressbook-level3/UserGuide.html) + 3. [AB3 Setting up and getting started page and related links](https://se-education.org/addressbook-level3/SettingUp.html) + 4. [AB3 Appendix: Requirements](https://se-education.org/addressbook-level3/DeveloperGuide.html#appendix-requirements) + 5. [AB2 Code Structure](https://github.com/se-edu/addressbook-level2) +2. [AY2021S2-CS2113-T10-1](https://github.com/AY2021S2-CS2113-T10-1) (Our TA, kwokyto's Team) + 1. [Developer Guide](https://github.com/AY2021S2-CS2113-T10-1/tp) + +### Terminologies + +| Terminology | Definition | +|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Data transformation | The conversion of one data format to another. | +| Application, Program | Refer to the `Decodex` program. This two terms are used interchangeably in this User Guide. | +| Encoding | Convert a message into a coded form. | +| Decoding | Convert a coded message into an intelligible form | +| Base64, Binary, Hexadecimal | Common types of data encoding standards. | +| Console | This refers to your command prompt window. | +| Argument | The additional information you provide to the program's command. | +| Module | A self-contained set of instructions to process your data into another form. | +| Recipe | Acts as a container for you to select your modules. When multiple modules are selected, this forms a "module chain". By default, you do not have any recipes. | + +### Symbols + +| Name | Definition | +|----------------------|-------------------------------------------------------------------------------------------------------| +| :bulb: | Represents a good tip for you. | +| :exclamation: | Represents something important that you should take note of. | +| :information_source: | Represents additional information regarding commands/features for you better understand how it works. | +| :pen: | Represents our rationale behind the design/implementation. | + +## Getting Started + +### Setting Up the Project 1. Forking and cloning 1. **Fork** our repository. 2. Then, **clone** the fork onto your computer. - > For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). - +> 💡 For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). +> 1. Editor 1. We highly recommend using Intellij IDEA, which can be downloaded from [here](https://www.jetbrains.com/idea/). 2. However, you may still use other editors that you prefer. Just take note that most of our set ups are centered around Intellij. 2. Configure JDK - 1. Follow the guide at *[[se-edu/guides] IDEA: Configuring the JDK](https://se-education.org/guides/tutorials/intellijJdk.html)* to ensure Intellij is configured to use **JDK 11**. + 1. Follow the guide at *[[se-edu/guides] IDEA: Configuring the JDK](https://se-education.org/guides/tutorials/intellijJdk.html)* to ensure Intellij is configured to use **JDK 11**, since our application runs on `Java 11`. 3. Importing project 1. Follow the guide at *[[se-edu/guides] IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html)* to import the forked project into Intellij. -> Note: Importing a Gradle project is slightly different from importing a normal Java project. -4. Verifying setup + +> ❗Note: Importing a Gradle project is slightly different from importing a normal Java project. +> +1. Verifying setup 1. Run the `decodex.Decodex.java` and try a few commands. 2. [Run the tests](https://se-education.org/addressbook-level3/Testing.html) to ensure they all pass. - -### Before writing code + + +### Additional Considerations 1. Configure the coding style - 1. If using IDEA, follow the guide [[se-edu/guides] IDEA: Configuring the code style](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA’s coding style to match ours. -> :bulb: Tip: Optionally, you can follow the guide [[se-edu/guides] Using Checkstyle](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems as you write code. -2. Set up CI - 1. This project comes with a GitHub Actions config files (in `.github/workflows` folder). When GitHub detects those files, it will run the CI for your project automatically at each push to the `master` branch or to any PR. No set up required. -3. About our code structure - 1. OOP standardized - 2. Modularized. - 1. Any changes/additions to the current commands would simply require the changes within `Parser.java`. - 2. For any changes/additions to the modules, would simply require changes within the `src/main/java/decodex/modules` folder. - 3. This structure makes it easier for us as well as developers like you to maintain and further extend the capabilities of our application. - + 1. If using IDEA, follow the guide [[se-edu/guides] IDEA: Configuring the code style](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA’s coding style to match ours. + > 💡 Tip: Optionally, you can follow the guide [[se-edu/guides] Using Checkstyle](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems as you write code. +1. Set up CI + 1. This project comes with a GitHub Actions config files (in `.github/workflows` folder). When GitHub detects those files, it will run the CI for your project automatically at each push to the `master` branch or to any PR. No set up required. +2. About our code structure + 1. OOP standardized + 2. Modularized. + 1. Any changes/additions to the current commands would simply require the changes within `Parser.java`. + 2. For any changes/additions to the modules, would simply require changes within the `src/main/java/decodex/modules` folder. + 3. This structure makes it easier for us as well as developers like you to maintain and further extend the capabilities of our application. + ## Design ### Architecture @@ -107,14 +136,55 @@ The rest of the program consists of 6 other components: ### Storage Component -## Documentation, Logging, Testing, Configuration and Dev-Ops - ## Appendix A: Product Scope +### Target User Profile + +1. Prefer using CLI over other types +2. Can type fast +3. Does Capture-the-flag (CTF) competitions +4. Requires to use multiple data manipulation techniques at once +5. Is comfortable with CLI +6. May be for both normal and expert users (in terms of technical capabilities) + +### Value Proposition + +This application helps users (mainly CTF players) to quickly transform data from one format to another (e.g., from plain text to base64-encoded text). It includes features such as the ability to perform basic data transformations with a few simple commands. Furthermore, it also includes the use of recipes to allow for multiple modules to be executed in sequence, which would be useful when multiple consecutive data transformations are needed. + +To sum it up, this application helps users to reduce the time needed to transform data from one form to another, especially when consecutive data transformations are required. + ## Appendix B: User Stories -## Appendix C: Non Functional Requirements +| version | priority | as a ... | I want to... | so that i can ... | +|----|----|----|---|----| +| V1.0 | *** | user | input data | perform data manipulation on it | +| V1.0 | *** | user | see the output of my processed data | see the effects of the change | +| V1.0 | *** | user | view a list of modules | see what modules I can use on my data | +| V1.0 | *** | user | add a module | decided to process data with the selected module | +| V1.0 | *** | user | run a module | process data with the selected module | +| V2.0| *** | user | create a new recipe | create different combinations of module chains | +| V2.0 | *** | user | view a list of modules while creating recipe | decide what modules to be added to the recipe | +| V2.0 | *** | user | add a module to the recipe | use it process my data | +| V1.0 | ** | user | remove all modules in the recipe | see my original input data | +| V2.0 | ** | expert user | read input data from a file | process data that are not printable or terminal-friendly | +| V2.0 | ** | expert user | edit the exported recipes | inspect and modify it in an editor | +| V2.0 | ** | user | see the list of the commands | know what commands I can use | +| V2.0 | ** | user | see the syntax of the commands | know how to use the commands | +| V2.0 | ** | CTF Participant | save my decoded output | reuse the output later | +| V2.0 | ** | user | import recipes from a file | I do not have to create the recipe from scratch/manually | +| V2.0 | ** | user | save my recipes to a file | I can reuse the recipe on a different computer | +| V2.0 | ** | user | list the recipes I have | use them again | + +## Appendix C: Non-Functional Requirements + +1. Should work on any *mainstream OS* as long as it has Java `11` or above installed. +2. Should be able to have up to 20 recipes without any impact on the performance. +3. A CTF participant should be able to work more efficiently on their CTF challenges compared to manual scripting in terms of time. +4. A user should be able to comfortably use and understand the application if they are within the IT field. +5. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. ## Appendix D: Glossary -## Appendix E: Instructions for Manual Testing +- **Mainstream OS**: Windows, Linux, Unix, OS-X + +## Appendix E: Instructions for Manual Testing \ No newline at end of file From a69fd9706c4e1e3f42a808a2c5be586256c4e42c Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 27 Oct 2021 01:53:19 +0800 Subject: [PATCH 365/607] Update DG with Design --- docs/DeveloperGuide.md | 116 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 7 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 9658d0342d..2d4b174dcc 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -74,39 +74,141 @@ ## Design -### Architecture +## Design -![Architecture diagram](images/architecture.png "Architecture diagram") +### Architecture -The ***Architecture Diagram*** given above explains the high-level design of Decodex. +The ***Architecture Diagram*** given below shows the high-level design of Decodex. -Given below is a quick overview of the main components and how they interact with one another. +![architecture.png](images/dg/architecture.png) -**Main Components of the architecture** +**Main Components of The Architecture** -`Decodex` is responsible initialising the components at launch. +`Decodex` is responsible for initialising the components at launch. The rest of the program consists of 6 other components: - `UI`: Handles user input and message output to the console - `Logic`: Parses user input and executes commands -- `Recipe`: Manages a list of module sequences +- `Recipe`: Manages a sequence of module to be executed - `Module`: Manages a set of encoding and decoding processes - `Data`: Holds the data that is to be encoded or decoded - `Storage`: Manages the reading and writing of data to disk ### UI Component +![UiClass.png](images/dg/UiClass.png) + +The `Ui` component consists of: + +- `Ui`: Manages access to the `Scanner` object that reads user input and also contains all the methods for printing to the user. + ### Logic Component +Below is a partial class diagram that shows an overview of the `Logic` component. + +![LogicComponent(4).png](images/dg/LogicComponent.png) + +The `Logic` component consists of: + +- `Parser`: Handles user input and decides the `Command` object to create. +- `Command`: An abstract class that defines the blueprint for the derived `*Command` classes. + - `InputCommand`: Takes in a string from the user and sets it as the current `Data` object to perform operations on. + - `HelpCommand`: Displays all command syntaxes to the user. + - `ListCommand`: Displays all `Module` objects and loaded `Recipe` objects to the user. + - `SelectCommand`: Executes a supported `Module` object or a loaded `Recipe` object on the current `Data` object and replaces it with the resulting `Data` object from the execution. + - `ResetCommand`: Reverts the current `Data` object to a state before any modules/recipes were executed on it. + - `ExitCommand`: Exits the application. + - `RecipeNewCommand`: Creates a new `Recipe` object with name provided by the user and creates a save file for it on the file system. + - `RecipeSelectCommand`: Set a `Recipe` object as the recipe that is currently being edited. + - `RecipeListCommand`: Display all `Module` objects in a `Recipe` object. + - `RecipePushCommand`: Appends a `Module` object to the `Recipe` object that is currently being edited. + - `RecipePopCommand`: Removes the latest `Module` object from the `Recipe` object that is currently being edited. + - `RecipeResetCommand`: Removes all `Module` objects from the `Recipe` object that is currently being edited. + - `RecipeDeleteCommand`: Deletes a `Recipe` object from the application as well as its corresponding save file on the file system. + + +Below is the class diagram showing the association between the `Decodex` class and the `Parser` class. + +![ParserClass.png](images/dg/ParserClass.png) + +Below is the class diagram showing the association between the abstract `Command` class and its derived `*Command` classes. + +![CommandClass1.png](images/dg/CommandClass1.png) + +![CommandClass2.png](images/dg/CommandClass2.png) ### Data Component +Below is a partial class diagram that shows an overview of the `Data` component. + +![DataComponent.png](images/dg/DataComponent.png) + +The `Data` component consists of: + +1. `Data`: Used to hold the bytes which the data transformations will act on. +2. `DataManager`: Holds a reference to the current `Data` object and updates it when a `Module` or `Recipe` is executed. + +Below is the class diagram showing the association between the `Decodex` class, the `DataManager` class and the `Data` class. + +![DataClass.png](images/dg/DataClass.png) + ### Module Component +Below is a partial class diagram that shows an overview of the `Module` component. + +![ModuleComponent.png](images/dg/ModuleComponent.png) + +The `Module` component consists of: + +- `Module`: An abstract class that defines the blueprint for the derived `*Encoder` or `*Decoder` classes. + - `Base64Encoder`, `Base64Decoder`: Performs base64 encoding/decoding operations + - `HexEncoder`, `HexDecoder`: Performs hexadecimal encoding/decoding operations + - `BinaryEncoder`, `BinaryDecoder`: Performs binary encoding/decoding operations + - `RotEncoder`: Performs rotational cipher operation +- `ModuleManager`: Decides and generates the necessary `Module` objects to be added to `Recipe` objects or to be executed on `Data` objects. + +Below is the class diagram showing the association between the `Decodex` class, the `ModuleManager` class. + +![ModuleManagerClass.png](images/dg/ModuleManagerClass.png) + +Below is the class diagram showing the association between the abstract `Module` class and its derived `*Decoder` classes. + +![ModuleClass.png](images/dg/ModuleClass.png) + ### Recipe Component +Below is a partial class diagram that shows an overview of the `Recipe` component. + +![RecipeComponent.png](images/dg/RecipeComponent.png) + +The `Recipe` component consists of: + +- `Recipe`: Holds a chain of `Module` objects that, when ran, will execute sequentially on a `Data` object. +- `RecipeManager`: Manages all loaded `Recipe` objects and holds a reference to the `Recipe` object that is currently being edited. + +![RecipeClass.png](images/dg/RecipeClass.png) + +> :pen: The reason for having `Recipe` objects is to enable users to run multiple modules at one go sequentially. +> + ### Storage Component +![StorageClass.png](images/dg/StorageClass.png) + +The `Storage` component consists of: + +- `Storage` : Performs all file related operations such as reading/saving to files and deleting of files. + +To add on, the `Storage` component is designed to access only the following folders: + +1. `recipe/` : For recipe files. +2. `output/` : For output data files. +3. `input/` : For input data file. + +> :pen: The rationale behind standardizing the specific folders to read/save to, is to ensure that all relevant files can be found in the same location, which makes it easier for users to find the files they are looking for. +> + + ## Documentation, Logging, Testing, Configuration and Dev-Ops ## Appendix A: Product Scope From 27cc73776af7bcb08c3267f2b3d84c776d98f043 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 27 Oct 2021 01:57:03 +0800 Subject: [PATCH 366/607] Add Recipe Command Implementation --- docs/DeveloperGuide.md | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index b11794eb32..569eeb7eb6 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -136,6 +136,89 @@ The rest of the program consists of 6 other components: ### Storage Component +## Recipe Commands + +The following commands are specific to managing modules within recipes. + +The command format begins with the command keyword `recipe`, followed by a subcommand keyword, and finally any arguments. + +Format: `recipe {arguments}` + +Example: `recipe new newRecipe` + +### RecipeNewCommand + +![RecipeNewCommand](images/dg/RecipeNewCommand.png) + +When the `RecipeCommandParser` recognises the `new` subcommand keyword from the user input, a `RecipeNewCommand` is instantiated. + +1. Creates a new `Recipe` object with the provided `recipeName`. +2. Add the newly created `Recipe` into the `RecipeManager`. +3. Sets `recipeName` as the `editingRecipeName` in `RecipeManager`. +4. Prints a successful creation message to the console. + +### RecipeSelectCommand + +![RecipeSelectCommand](images/dg/RecipeSelectCommand.png) + +When the `RecipeCommandParser` recognises the `select` subcommand keyword from the user input, a `RecipeSelectCommand` is instantiated. + +1. Retrieves the `Recipe` corresponding with the provided `recipeName` from `RecipeManager`. +2. Sets `recipeName` as the `editingRecipeName` in `RecipeManager`. +3. Prints a successful selection message containing the `recipeName` to the console. + +### RecipeListCommand + +![RecipeListCommand](images/dg/RecipeListCommand.png) + +When the `RecipeCommandParser` recognises the `list` subcommand keyword from the user input, a `RecipeListCommand` is instantiated. + +1. If `recipeName` is + 1. blank, the `Recipe` with the current `editingRecipeName` is retrieved. + 2. not blank, the `Recipe` with `recipeName` is retrieved. +2. Retrieves the list of `Module` objects belonging to the `Recipe` with `recipeName`. +3. Prints the names and parameters of each `Module` object in the retrieved list. + +### RecipePushCommand + +![RecipePushCommand](images/dg/RecipePushCommand.png) + +When the `RecipeCommandParser` recognises the `push` subcommand keyword from the user input, a `RecipePushCommand` is instantiated. + +1. Retrieves the corresponding `Module` with the provided `moduleName` and `parameters` from `ModuleManager`. +2. Retrieves the current editing `Recipe`. +3. Adds the retrieved `Module` into the current editing `Recipe`. +4. Prints the a message of the added `Module` to the console. + +### RecipePopCommand + +![RecipePopCommand](images/dg/RecipePopCommand.png) + +When the `RecipeCommandParser` recognises the `pop` subcommand keyword from the user input, a `RecipePopCommand` is instantiated. + +1. Retrieves the latest `Module` added to the `Recipe`, and removes it from the `Recipe` after retrieval. +2. Retrieves the current editing `Recipe`. +3. Prints the a message of the removed `Module` to the console. + +### RecipeResetCommand + +![RecipeResetCommand](images/dg/RecipeResetCommand.png) + +When the `RecipeCommandParser` recognises the `Reset` subcommand keyword from the user input, a `RecipeResetCommand` is instantiated. + +1. Retrieves the `Recipe` with the current `editingRecipeName` is retrieved. +2. Removes all modules contained in the `Recipe` +3. Prints a successful reset message containing the `recipeName` to the console. + +### RecipeDeleteCommand + +![RecipeDeleteCommand](images/dg/RecipeDeleteCommand.png) + +When the `RecipeCommandParser` recognises the `delete` subcommand keyword from the user input, a `RecipeDeleteCommand` is instantiated. + +1. Removes the `Recipe` with the `recipeName` from `RecipeManager`. +2. Prints `recipeName` as the deleted `Recipe` to the console. + ## Appendix A: Product Scope ### Target User Profile From ce96026437a4512a31e389548e1e80e74ae553d3 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 02:13:33 +0800 Subject: [PATCH 367/607] Update DG with Implementation --- docs/DeveloperGuide.md | 264 +++++++++++++++++++++++++++++++++++------ 1 file changed, 229 insertions(+), 35 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 5524ef70b2..b0f8130888 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -16,18 +16,56 @@ The purpose of this guide is to provide more information on our application, Dec This developer guide is made for developers who wish to further understand and/or develop **Decodex**. This guide includes the setup instructions, design, implementation, testing, product scope, and other sections to give developers a better understanding of the application. -## Table of Contents -| Terminology | Definition | -|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Data transformation | The conversion of one data format to another. | -| Application, Program | Refer to the `Decodex` program. This two terms are used interchangeably in this User Guide. | -| Encoding | Convert a message into a coded form. | -| Decoding | Convert a coded message into an intelligible form | -| Base64, Binary, Hexadecimal | Common types of data encoding standards. | -| Console | This refers to your command prompt window. | -| Argument | The additional information you provide to the program's command. | -| Module | A self-contained set of instructions to process your data into another form. | -| Recipe | Acts as a container for you to select your modules. When multiple modules are selected, this forms a "module chain". By default, you do not have any recipes. | +## Table of Contents +- [Introduction](#introduction) + - [Purpose of This Guide](#purpose-of-this-guide) + - [Developer Guide Usage](#developer-guide-usage) +- [Acknowledgements](#acknowledgements) + - [Terminologies](#terminologies) + - [Symbols](#symbols) +- [Getting Started](#getting-started) + - [Setting Up the Project](#setting-up-the-project) + - [Additional Considerations](#additional-considerations) +- [Design](#design) +- [Design](#design-1) + - [Architecture](#architecture) + - [UI Component](#ui-component) + - [Logic Component](#logic-component) + - [Data Component](#data-component) + - [Module Component](#module-component) + - [Recipe Component](#recipe-component) + - [Storage Component](#storage-component) +- [Implementation](#implementation) + - [Decodex Initialisation](#decodex-initialisation) + - [Decodex Main Logic](#decodex-main-logic) + - [Parser - Basic Command Logic](#parser---basic-command-logic) + - [Parser - Recipe Command Logic](#parser---recipe-command-logic) + - [Modules](#modules) + - [Implemented Modules:](#implemented-modules) + - [List of Commands](#list-of-commands) + - [InputCommand](#inputcommand) + - [ExitCommand](#exitcommand) + - [ResetCommand](#resetcommand) + - [SelectCommand](#selectcommand) + - [SelectCommand (Module)](#selectcommand-module) + - [SelectCommand (Recipe)](#selectcommand-recipe) + - [ListCommand](#listcommand) +- [Recipe Commands](#recipe-commands) + - [RecipeNewCommand](#recipenewcommand) + - [RecipeSelectCommand](#recipeselectcommand) + - [RecipeListCommand](#recipelistcommand) + - [RecipePushCommand](#recipepushcommand) + - [RecipePopCommand](#recipepopcommand) + - [RecipeResetCommand](#reciperesetcommand) + - [RecipeDeleteCommand](#recipedeletecommand) +- [Appendix A: Product Scope](#appendix-a-product-scope) + - [Target User Profile](#target-user-profile) + - [Value Proposition](#value-proposition) +- [Appendix B: User Stories](#appendix-b-user-stories) +- [Appendix C: Non-Functional Requirements](#appendix-c-non-functional-requirements) +- [Appendix D: Glossary](#appendix-d-glossary) +- [Appendix E: Instructions for Manual Testing](#appendix-e-instructions-for-manual-testing) + ## Acknowledgements 1. SE-EDU @@ -36,13 +74,12 @@ This guide includes the setup instructions, design, implementation, testing, pro 3. [AB3 Setting up and getting started page and related links](https://se-education.org/addressbook-level3/SettingUp.html) 4. [AB3 Appendix: Requirements](https://se-education.org/addressbook-level3/DeveloperGuide.html#appendix-requirements) 5. [AB2 Code Structure](https://github.com/se-edu/addressbook-level2) -2. [AY2021S2-CS2113-T10-1](https://github.com/AY2021S2-CS2113-T10-1) (Our TA, kwokyto's Team) - 1. [Developer Guide](https://github.com/AY2021S2-CS2113-T10-1/tp) +2. [AY2021S2-CS2113-T10-1's Developer Guide](https://ay2021s2-cs2113-t10-1.github.io/tp/DeveloperGuide.html) ### Terminologies | Terminology | Definition | -|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Data transformation | The conversion of one data format to another. | | Application, Program | Refer to the `Decodex` program. This two terms are used interchangeably in this User Guide. | | Encoding | Convert a message into a coded form. | @@ -56,7 +93,7 @@ This guide includes the setup instructions, design, implementation, testing, pro ### Symbols | Name | Definition | -|----------------------|-------------------------------------------------------------------------------------------------------| +| -------------------- | ----------------------------------------------------------------------------------------------------- | | :bulb: | Represents a good tip for you. | | :exclamation: | Represents something important that you should take note of. | | :information_source: | Represents additional information regarding commands/features for you better understand how it works. | @@ -239,6 +276,163 @@ To add on, the `Storage` component is designed to access only the following fold ## Implementation +This section focuses on explaining the specific applications flows and the interactions between the classes and their methods. + +💡 The lifeline of the objects should terminate at the destroy marker (`X`), however due to a limitation of the PlantUML software, the lifeline extends beyond the destroy marker. + +### Decodex Initialisation + +Below shows the sequence diagram of the initialisation of Decodex. + +![Decodex Initialisation](images/dg/DecodexInitSeq.png) + +During the initialisation sequence of Decodex, all the dependencies (i.e `DataManager`, `ModuleManager` .etc) are instantiated, which will later on be passed to the relevant commands and methods for usage. + +### Decodex Main Logic + +Below is the sequence diagram showing the overall flow of Decodex. + +![Decodex Main Logic](images/dg/DecodexMainSeq.png) + +This main logic occurs after the initialisation sequence in the previous figure. It comprises of the main loop that provides the interactivity to the user by reading input from them and attempting to execute it as a command. The loop terminates when the user enters the `exit` command. + +### Parser - Basic Command Logic + +Below is the sequence diagram that shows the overall flow of the `Parser` logic. + +![Parser Basic Command Logic](images/dg/ParserBasicSeq.png) + +The flow of the `Parser` logic: + +1. Receives the user input and parses it to get the command type. +2. Prepares the corresponding `XYZCommand`. + 1. However, if the command is a `Recipe*Command` , it will be passed to `RecipeCommandParser` for subcommand parsing. +3. Returns the command back to `Decodex`. + +### Parser - Recipe Command Logic + +Below is the sequence diagram that shows the overall flow of the `RecipeCommandParser` logic. + +![Parser Recipe Command Logic](images/dg/ParserRecipeSeq.png) + +The general process of the `RecipeCommandParser` logic consists of: + +1. Receives the user input from `Parser` then parses it to get the subcommand type. +2. Prepares the corresponding `RecipeXYZCommand` +3. Returns the command back to `Decodex` + +### Modules + +The abstract `Module` class serves as the base class for all modules to be inherited from. It contains essential information that must be present in every module developed for Decodex. + +- `name`: The name of the module +- `description`: The description of the module +- `run(Data data)`: The abstract method to process the provided `Data` + +The `name` and `description` variables must be set during the instantiation of the module, that is to say in the constructor. + +The abstract `run(Data data)` method should be implemented to return a `Data` object as the purpose of this method is solely for data processing. + +There are two groups of modules supported by Decodex: + +- Modules that do not take in any parameters +- Modules that take in at least one parameter + +Modules that do not take in any parameters + +This group of modules do not require any additional parameters as their encoding and decoding processes are fixed. Hence, the constructors of these modules should not receive any parameters. + +Modules that take in at least one parameter + +This group of modules require at least one additional parameter as their encoding and decoding processes are dependent on the provided parameters. The constructors of these modules should receive parameters as required and stored in private variables. These variables will then be used in their respective `run(Data data)` method. + +The current implementation of the abstract `Module` class provides a strong foundation top be inherited by much more complex modules, and developed into full-functioning modules for Decodex. + +#### Implemented Modules: + +| No Parameters | At Least One Parameter | +| -------------------------------------------------------------------------------------------- | ---------------------- | +| Base64Encoder
Base64Decoder
HexEncoder
HexDecoder
BinaryEncoder
BinaryDecoder | RotEncoder | + +### List of Commands + +#### InputCommand + +![InputCommand](images/dg/InputCommand.png) + +When the `Parser` recognises the `input` keyword from the user input, an `InputCommand` is instantiated. + +1. Create a `Data` object from the user input. +2. Set the created `Data` object as the `originalData` in the `DataManager` . +3. Prints `input` to the console. + +#### ExitCommand + +![ExitCommand](images/dg/ExitCommand.png) + +When the `Parser` recognises the `exit` keyword from the user input, an `ExitCommand` is instantiated. + +1. Prints the goodbye message to the console. + +#### ResetCommand + +![ResetCommand](images/dg/ResetCommand.png) + +When the `Parser` recognises the `reset` keyword from the user input, a `ResetCommand` is instantiated. + +1. Replace the contents of `currentData` in `DataManager` with that of `originalData`. +2. Prints a successful reset message to the console. + +#### SelectCommand + +![SelectCommand](images/dg/SelectCommand.png) + +When the `Parser` recognises the `select` keyword from the user input, a `SelectCommand` is instantiated. + +1. If `selectCategory` is + 1. `module`, The selected `Module` is retrieved and executed on the provided `Data`. See [SelectCommand (Module)](#selectcommand-module) for the full sequence diagram. + 2. `recipe`, The selected `Recipe` is retrieved and its `Module`objects are executed on the provided `Data`. See [SelectCommand (Recipe)](#selectcommand-recipe) for the full sequence diagram. + +##### SelectCommand (Module) + +![SelectCommand](images/dg/SelectCommandModule.png) + +1. Retrieves the corresponding `Module` with the provided `itemName` and `parameters` from `ModuleManager`. +2. Retrieves the current `Data` object from `DataManager`. +3. Executes the selected `Module` on the retrieved `Data` object and stores the processed content in a new `Data` object. +4. Sets the new `Data` object as the current data in `DataManager`. +5. Prints the contents of the new `Data` object to the console. +6. Retrieves the current `Data` object from `DataManager`. +7. Executes the selected `Module` on the retrieved `Data` object and creates a new `Data` object. +8. Sets the new `Data` object as the current data in `DataManager`. +9. Prints the content of the new `Data` object to the console. + +##### SelectCommand (Recipe) + +![SelectCommand (Recipe)](images/dg/SelectCommandRecipe.png) + +1. Retrieves the corresponding Recipe with the provided `itemName` from `RecipeManager`. +2. Retrieves the current `Data` object from `DataManager`. +3. Executes the selected `Recipe` on the retrieved `Data` object and stores the processed content in a new `Data` object. +4. Sets the new `Data` object as the current data in `DataManager`. +5. Prints the `Module`s in the `Recipe` to the console. +6. Prints the content of the new `Data` object to the console. + +#### ListCommand + +![ListCommand](images/dg/ListCommand.png) + +When the `Parser` recognises the `list` keyword from the user input, a `ListCommand` is instantiated. + +The `ListCommand` would then parse any optional arguments, setting the `isPrintModuleList` and `isPrintRecipeList` boolean variables appropriately. + +1. If `isPrintModuleList` is true + 1. Retrieves a list of all `Module` objects from `ModuleManager`. + 2. Prints the retrieved list of `Module` objects to the console. +2. If `isPrintRecipeList` is true + 1. Retrieves a list of all `Recipe` objects from `RecipeManager`. + 2. Prints the retrieved list of `Recipe` objects to the console. + ## Recipe Commands The following commands are specific to managing modules within recipes. @@ -341,25 +535,25 @@ To sum it up, this application helps users to reduce the time needed to transfor ## Appendix B: User Stories -| version | priority | as a ... | I want to... | so that i can ... | -|----|----|----|---|----| -| V1.0 | *** | user | input data | perform data manipulation on it | -| V1.0 | *** | user | see the output of my processed data | see the effects of the change | -| V1.0 | *** | user | view a list of modules | see what modules I can use on my data | -| V1.0 | *** | user | add a module | decided to process data with the selected module | -| V1.0 | *** | user | run a module | process data with the selected module | -| V2.0| *** | user | create a new recipe | create different combinations of module chains | -| V2.0 | *** | user | view a list of modules while creating recipe | decide what modules to be added to the recipe | -| V2.0 | *** | user | add a module to the recipe | use it process my data | -| V1.0 | ** | user | remove all modules in the recipe | see my original input data | -| V2.0 | ** | expert user | read input data from a file | process data that are not printable or terminal-friendly | -| V2.0 | ** | expert user | edit the exported recipes | inspect and modify it in an editor | -| V2.0 | ** | user | see the list of the commands | know what commands I can use | -| V2.0 | ** | user | see the syntax of the commands | know how to use the commands | -| V2.0 | ** | CTF Participant | save my decoded output | reuse the output later | -| V2.0 | ** | user | import recipes from a file | I do not have to create the recipe from scratch/manually | -| V2.0 | ** | user | save my recipes to a file | I can reuse the recipe on a different computer | -| V2.0 | ** | user | list the recipes I have | use them again | +| version | priority | as a ... | I want to... | so that i can ... | +| ------- | -------- | --------------- | -------------------------------------------- | -------------------------------------------------------- | +| V1.0 | *** | user | input data | perform data manipulation on it | +| V1.0 | *** | user | see the output of my processed data | see the effects of the change | +| V1.0 | *** | user | view a list of modules | see what modules I can use on my data | +| V1.0 | *** | user | add a module | decided to process data with the selected module | +| V1.0 | *** | user | run a module | process data with the selected module | +| V2.0 | *** | user | create a new recipe | create different combinations of module chains | +| V2.0 | *** | user | view a list of modules while creating recipe | decide what modules to be added to the recipe | +| V2.0 | *** | user | add a module to the recipe | use it process my data | +| V1.0 | ** | user | remove all modules in the recipe | see my original input data | +| V2.0 | ** | expert user | read input data from a file | process data that are not printable or terminal-friendly | +| V2.0 | ** | expert user | edit the exported recipes | inspect and modify it in an editor | +| V2.0 | ** | user | see the list of the commands | know what commands I can use | +| V2.0 | ** | user | see the syntax of the commands | know how to use the commands | +| V2.0 | ** | CTF Participant | save my decoded output | reuse the output later | +| V2.0 | ** | user | import recipes from a file | I do not have to create the recipe from scratch/manually | +| V2.0 | ** | user | save my recipes to a file | I can reuse the recipe on a different computer | +| V2.0 | ** | user | list the recipes I have | use them again | ## Appendix C: Non-Functional Requirements From 12742ebc556d003d8d1bff9b3370ba2f793ad877 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 27 Oct 2021 02:15:37 +0800 Subject: [PATCH 368/607] Fixed some of the unhandled exceptions. --- src/main/java/decodex/storage/Storage.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 3d1d68fd8b..8a1922dd7b 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -2,6 +2,7 @@ import decodex.data.exception.ModuleException; import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.modules.Module; @@ -88,7 +89,7 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager try { Recipe recipe = readRecipeFromFile(recipeName, recipeFile, moduleManager); recipeManager.addRecipe(recipe); - } catch (IOException | ModuleException | ModuleManagerException | RecipeManagerException err) { + } catch (IOException | ModuleException | ModuleManagerException | RecipeManagerException | RecipeException err) { String failedToLoadMessage = recipeName + ErrorMessages.FAILED_TO_LOAD_RECIPE_FILE_MESSAGE; errorsOfFailedRecipeLoads.add(failedToLoadMessage); } @@ -126,7 +127,7 @@ private File[] getAllRecipeFiles() { * @throws ModuleManagerException If provided module name is not an available module. */ private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, ModuleManager moduleManager) - throws ModuleException, ModuleManagerException { + throws ModuleException, ModuleManagerException, RecipeException { Recipe recipe = new Recipe(recipeFileName); String[] recipeLines = recipeContent.split(LINE_BREAK_REGEX); @@ -156,7 +157,7 @@ private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, * @throws ModuleManagerException If provided module name is not an available module. */ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleManager moduleManager) - throws IOException, ModuleException, ModuleManagerException { + throws IOException, ModuleException, ModuleManagerException, RecipeException { Path recipeFilePath = recipeFile.toPath(); byte[] recipeContentBytes = readContentFromFile(recipeFilePath); From 52b611602ff1a99cfb030fb67444ba1e210745fe Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 02:20:41 +0800 Subject: [PATCH 369/607] Minor fix --- docs/DeveloperGuide.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index b0f8130888..8f1d4bb831 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -8,8 +8,7 @@ Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CT The purpose of this guide is to provide more information on our application, Decodex, such as the overall architecture, implementation and design rationales to developers who wish to contribute and enhance Decodex to it's fullest potential. As of the release of this developer guide, it is written for Decodex V2.0. -> ℹ This guide may also serve as a start for software testers to find bugs and possibly edge cases within our applications. -> +> :information_source: This guide may also serve as a start for software testers to find bugs and possibly edge cases within our applications. ### Developer Guide Usage @@ -27,7 +26,6 @@ This guide includes the setup instructions, design, implementation, testing, pro - [Setting Up the Project](#setting-up-the-project) - [Additional Considerations](#additional-considerations) - [Design](#design) -- [Design](#design-1) - [Architecture](#architecture) - [UI Component](#ui-component) - [Logic Component](#logic-component) @@ -108,18 +106,18 @@ This guide includes the setup instructions, design, implementation, testing, pro 2. Then, **clone** the fork onto your computer. > 💡 For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). -> -1. Editor + +2. Editor 1. We highly recommend using Intellij IDEA, which can be downloaded from [here](https://www.jetbrains.com/idea/). 2. However, you may still use other editors that you prefer. Just take note that most of our set ups are centered around Intellij. -2. Configure JDK +3. Configure JDK 1. Follow the guide at *[[se-edu/guides] IDEA: Configuring the JDK](https://se-education.org/guides/tutorials/intellijJdk.html)* to ensure Intellij is configured to use **JDK 11**, since our application runs on `Java 11`. -3. Importing project +4. Importing project 1. Follow the guide at *[[se-edu/guides] IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html)* to import the forked project into Intellij. > ❗Note: Importing a Gradle project is slightly different from importing a normal Java project. -> -1. Verifying setup + +5. Verifying setup 1. Run the `decodex.Decodex.java` and try a few commands. 2. [Run the tests](https://se-education.org/addressbook-level3/Testing.html) to ensure they all pass. @@ -129,9 +127,9 @@ This guide includes the setup instructions, design, implementation, testing, pro 1. Configure the coding style 1. If using IDEA, follow the guide [[se-edu/guides] IDEA: Configuring the code style](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA’s coding style to match ours. > 💡 Tip: Optionally, you can follow the guide [[se-edu/guides] Using Checkstyle](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems as you write code. -1. Set up CI +2. Set up CI 1. This project comes with a GitHub Actions config files (in `.github/workflows` folder). When GitHub detects those files, it will run the CI for your project automatically at each push to the `master` branch or to any PR. No set up required. -2. About our code structure +3. About our code structure 1. OOP standardized 2. Modularized. 1. Any changes/additions to the current commands would simply require the changes within `Parser.java`. @@ -140,8 +138,6 @@ This guide includes the setup instructions, design, implementation, testing, pro ## Design -## Design - ### Architecture The ***Architecture Diagram*** given below shows the high-level design of Decodex. @@ -255,7 +251,6 @@ The `Recipe` component consists of: ![RecipeClass.png](images/dg/RecipeClass.png) > :pen: The reason for having `Recipe` objects is to enable users to run multiple modules at one go sequentially. -> ### Storage Component @@ -272,7 +267,6 @@ To add on, the `Storage` component is designed to access only the following fold 3. `input/` : For input data file. > :pen: The rationale behind standardizing the specific folders to read/save to, is to ensure that all relevant files can be found in the same location, which makes it easier for users to find the files they are looking for. -> ## Implementation From 341dd60785a275e9115cc7e3709a9a16b6d7fa81 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 27 Oct 2021 02:22:04 +0800 Subject: [PATCH 370/607] Fix checkStyleMain --- src/main/java/decodex/storage/Storage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 8a1922dd7b..b81a59c10d 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -89,7 +89,8 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager try { Recipe recipe = readRecipeFromFile(recipeName, recipeFile, moduleManager); recipeManager.addRecipe(recipe); - } catch (IOException | ModuleException | ModuleManagerException | RecipeManagerException | RecipeException err) { + } catch (IOException | ModuleException | ModuleManagerException + | RecipeManagerException | RecipeException err) { String failedToLoadMessage = recipeName + ErrorMessages.FAILED_TO_LOAD_RECIPE_FILE_MESSAGE; errorsOfFailedRecipeLoads.add(failedToLoadMessage); } From 43d7803cc87ddbcb99a8e426b312394648ae1838 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 02:29:46 +0800 Subject: [PATCH 371/607] Refactor code for simplicity * Rename `hasNoParameters` to `ensureNoParameters` * Simplify preparation of `RotEncoder` by moving offset into `rotOffset` --- .../java/decodex/modules/ModuleManager.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index d9889ec418..fef1663bc1 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -98,7 +98,7 @@ public Module selectModule(String moduleName, String[] parameters) throws Module * @param parameters Parameters for specific commands. * @throws ModuleException If the String array of module parameters is not empty. */ - private void hasNoParameters(String[] parameters) throws ModuleException { + private void ensureNoParameters(String[] parameters) throws ModuleException { if (parameters.length > 0) { throw new ModuleException(ErrorMessages.TOO_MANY_MODULE_PARAMETERS); } @@ -111,7 +111,7 @@ private void hasNoParameters(String[] parameters) throws ModuleException { * @throws ModuleException If module parameters are provided. */ private Module prepareBase64Encoder(String[] parameters) throws ModuleException { - hasNoParameters(parameters); + ensureNoParameters(parameters); return new Base64Encoder(); } @@ -122,7 +122,7 @@ private Module prepareBase64Encoder(String[] parameters) throws ModuleException * @throws ModuleException If module parameters are provided. */ private Module prepareBase64Decoder(String[] parameters) throws ModuleException { - hasNoParameters(parameters); + ensureNoParameters(parameters); return new Base64Decoder(); } @@ -133,7 +133,7 @@ private Module prepareBase64Decoder(String[] parameters) throws ModuleException * @throws ModuleException If module parameters are provided. */ private Module prepareHexEncoder(String[] parameters) throws ModuleException { - hasNoParameters(parameters); + ensureNoParameters(parameters); return new HexEncoder(); } @@ -144,7 +144,7 @@ private Module prepareHexEncoder(String[] parameters) throws ModuleException { * @throws ModuleException If module parameters are provided. */ private Module prepareHexDecoder(String[] parameters) throws ModuleException { - hasNoParameters(parameters); + ensureNoParameters(parameters); return new HexDecoder(); } @@ -155,7 +155,7 @@ private Module prepareHexDecoder(String[] parameters) throws ModuleException { * @throws ModuleException If module parameters are provided. */ private Module prepareBinaryEncoder(String[] parameters) throws ModuleException { - hasNoParameters(parameters); + ensureNoParameters(parameters); return new BinaryEncoder(); } @@ -166,7 +166,7 @@ private Module prepareBinaryEncoder(String[] parameters) throws ModuleException * @throws ModuleException If module parameters are provided. */ private Module prepareBinaryDecoder(String[] parameters) throws ModuleException { - hasNoParameters(parameters); + ensureNoParameters(parameters); return new BinaryDecoder(); } @@ -186,7 +186,8 @@ private Module prepareRotEncoder(String[] parameters) throws ModuleException { // First parameter is the rotation offset try { - return new RotEncoder(Integer.parseInt(parameters[MODULE_PARAMETER_INDEX])); + int rotOffset = Integer.parseInt(parameters[MODULE_PARAMETER_INDEX]); + return new RotEncoder(rotOffset); } catch (NumberFormatException err) { throw new ModuleException(ErrorMessages.INVALID_MODULE_PARAMETERS); } From 4e03d5a0316f174411b5283b3950169530ef1eb3 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 27 Oct 2021 02:37:55 +0800 Subject: [PATCH 372/607] Add newline --- src/test/java/decodex/commands/HelpCommandTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/decodex/commands/HelpCommandTest.java b/src/test/java/decodex/commands/HelpCommandTest.java index 5f7a12dcac..3c1602e97d 100644 --- a/src/test/java/decodex/commands/HelpCommandTest.java +++ b/src/test/java/decodex/commands/HelpCommandTest.java @@ -41,4 +41,4 @@ public void run_allCommands_success() { command.run(dataManager, moduleManager, ui, recipeManager); assertFalse(outputStream.toString().isBlank()); } -} \ No newline at end of file +} From b3be4a68f8214a2567132edaf9f008629878c6af Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 27 Oct 2021 02:50:28 +0800 Subject: [PATCH 373/607] Fixed the recipecommand headers. --- docs/DeveloperGuide.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 8f1d4bb831..37014495dd 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -48,14 +48,14 @@ This guide includes the setup instructions, design, implementation, testing, pro - [SelectCommand (Module)](#selectcommand-module) - [SelectCommand (Recipe)](#selectcommand-recipe) - [ListCommand](#listcommand) -- [Recipe Commands](#recipe-commands) - - [RecipeNewCommand](#recipenewcommand) - - [RecipeSelectCommand](#recipeselectcommand) - - [RecipeListCommand](#recipelistcommand) - - [RecipePushCommand](#recipepushcommand) - - [RecipePopCommand](#recipepopcommand) - - [RecipeResetCommand](#reciperesetcommand) - - [RecipeDeleteCommand](#recipedeletecommand) + - [Recipe Commands](#recipe-commands) + - [RecipeNewCommand](#recipenewcommand) + - [RecipeSelectCommand](#recipeselectcommand) + - [RecipeListCommand](#recipelistcommand) + - [RecipePushCommand](#recipepushcommand) + - [RecipePopCommand](#recipepopcommand) + - [RecipeResetCommand](#reciperesetcommand) + - [RecipeDeleteCommand](#recipedeletecommand) - [Appendix A: Product Scope](#appendix-a-product-scope) - [Target User Profile](#target-user-profile) - [Value Proposition](#value-proposition) @@ -105,7 +105,7 @@ This guide includes the setup instructions, design, implementation, testing, pro 1. **Fork** our repository. 2. Then, **clone** the fork onto your computer. -> 💡 For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). + > 💡 For convenience, our repository can be found [here](https://github.com/AY2122S1-CS2113T-T10-3/tp). 2. Editor 1. We highly recommend using Intellij IDEA, which can be downloaded from [here](https://www.jetbrains.com/idea/). @@ -115,7 +115,7 @@ This guide includes the setup instructions, design, implementation, testing, pro 4. Importing project 1. Follow the guide at *[[se-edu/guides] IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html)* to import the forked project into Intellij. -> ❗Note: Importing a Gradle project is slightly different from importing a normal Java project. + > ❗Note: Importing a Gradle project is slightly different from importing a normal Java project. 5. Verifying setup 1. Run the `decodex.Decodex.java` and try a few commands. @@ -427,7 +427,7 @@ The `ListCommand` would then parse any optional arguments, setting the `isPrintM 1. Retrieves a list of all `Recipe` objects from `RecipeManager`. 2. Prints the retrieved list of `Recipe` objects to the console. -## Recipe Commands +### Recipe Commands The following commands are specific to managing modules within recipes. @@ -437,7 +437,7 @@ Format: `recipe {arguments}` Example: `recipe new newRecipe` -### RecipeNewCommand +#### RecipeNewCommand ![RecipeNewCommand](images/dg/RecipeNewCommand.png) @@ -448,7 +448,7 @@ When the `RecipeCommandParser` recognises the `new` subcommand keyword from the 3. Sets `recipeName` as the `editingRecipeName` in `RecipeManager`. 4. Prints a successful creation message to the console. -### RecipeSelectCommand +#### RecipeSelectCommand ![RecipeSelectCommand](images/dg/RecipeSelectCommand.png) @@ -458,7 +458,7 @@ When the `RecipeCommandParser` recognises the `select` subcommand keyword from t 2. Sets `recipeName` as the `editingRecipeName` in `RecipeManager`. 3. Prints a successful selection message containing the `recipeName` to the console. -### RecipeListCommand +#### RecipeListCommand ![RecipeListCommand](images/dg/RecipeListCommand.png) @@ -470,7 +470,7 @@ When the `RecipeCommandParser` recognises the `list` subcommand keyword from the 2. Retrieves the list of `Module` objects belonging to the `Recipe` with `recipeName`. 3. Prints the names and parameters of each `Module` object in the retrieved list. -### RecipePushCommand +#### RecipePushCommand ![RecipePushCommand](images/dg/RecipePushCommand.png) @@ -481,7 +481,7 @@ When the `RecipeCommandParser` recognises the `push` subcommand keyword from the 3. Adds the retrieved `Module` into the current editing `Recipe`. 4. Prints the a message of the added `Module` to the console. -### RecipePopCommand +#### RecipePopCommand ![RecipePopCommand](images/dg/RecipePopCommand.png) @@ -491,7 +491,7 @@ When the `RecipeCommandParser` recognises the `pop` subcommand keyword from the 2. Retrieves the current editing `Recipe`. 3. Prints the a message of the removed `Module` to the console. -### RecipeResetCommand +#### RecipeResetCommand ![RecipeResetCommand](images/dg/RecipeResetCommand.png) @@ -501,7 +501,7 @@ When the `RecipeCommandParser` recognises the `Reset` subcommand keyword from th 2. Removes all modules contained in the `Recipe` 3. Prints a successful reset message containing the `recipeName` to the console. -### RecipeDeleteCommand +#### RecipeDeleteCommand ![RecipeDeleteCommand](images/dg/RecipeDeleteCommand.png) From 92b4244e9f9ba9cc826916d9d9e77378b86c0783 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 03:12:40 +0800 Subject: [PATCH 374/607] Fix to print module parameters --- .../commands/recipe/RecipeListCommand.java | 2 +- src/main/java/decodex/ui/Ui.java | 2 +- text-ui-test/EXPECTED.TXT | 56 ++++++++++--------- text-ui-test/input.txt | 2 + 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/main/java/decodex/commands/recipe/RecipeListCommand.java b/src/main/java/decodex/commands/recipe/RecipeListCommand.java index 4b07a75046..2fb27dd8bf 100644 --- a/src/main/java/decodex/commands/recipe/RecipeListCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeListCommand.java @@ -54,7 +54,7 @@ private String getModuleList(Recipe recipe) { return RegularMessages.RECIPE_EMPTY; } - String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); + String[] recipeModuleNames = recipeModuleList.stream().map(Module::toString).toArray(String[]::new); StringBuilder recipeListString = new StringBuilder(); for (int index = 1; index <= recipeModuleNames.length; index++) { diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index a64a941cd5..4cf8aac6d8 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -106,7 +106,7 @@ public void printRecipeModules(Recipe recipe) { } ArrayList recipeModuleList = recipe.getModuleList(); - String[] recipeModuleNames = recipeModuleList.stream().map(Module::getName).toArray(String[]::new); + String[] recipeModuleNames = recipeModuleList.stream().map(Module::toString).toArray(String[]::new); String message = RegularMessages.RECIPE_MODULES_EXECUTED + " " + String.join(", ", recipeModuleNames); printSuccess(message); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index b726f771c3..83c38a4b5f 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,17 +1,17 @@ ======================== Decodex =========================== Welcome to Decodex, transforming data with ease -============================================================ -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Your command cannot be blank -Decodex > [+] Input: "I am a lab rat" -Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "4920616d2061206c616220726174" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "L dp d ode udw" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Reverted all changes +============================================================ +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Your command cannot be blank +Decodex > [+] Input: "I am a lab rat" +Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "4920616d2061206c616220726174" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "L dp d ode udw" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Reverted all changes Decodex > Here is the list of supported modules: base64encode - Encodes the data using Base64 format. base64decode - Decodes the data using Base64 format. @@ -20,20 +20,24 @@ Decodex > Here is the list of supported modules: binencode - Encodes the data into binary. bindecode - Decodes the data from binary. rotencode - Rotates the alphabets in the input string by a specified amount - + You do not have any recipes - -Decodex > [x] The given recipe could not be found. -Decodex > [+] Recipe testRecipe created successfully + +Decodex > [x] The given recipe could not be found. +Decodex > [+] Recipe testRecipe created successfully Decodex > Here is the list of available recipes: testRecipe - -Decodex > [+] There are no modules in the recipe -[+] Output: "I am a lab rat" -Decodex > [+] Added hexencode module to recipe testRecipe -Decodex > [+] Added base64encode module to recipe testRecipe -Decodex > [+] Added rotencode module to recipe testRecipe -Decodex > [+] Modules executed: hexencode, base64encode, rotencode -[+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" -Decodex > [x] Unknown command, please enter a valid command -Decodex > Goodbye! + +Decodex > Here is the list of modules in recipe testRecipe: +There are no modules in the recipe +Decodex > Here is the list of modules in recipe testRecipe: +There are no modules in the recipe +Decodex > [+] There are no modules in the recipe +[+] Output: "I am a lab rat" +Decodex > [+] Added hexencode module to recipe testRecipe +Decodex > [+] Added base64encode module to recipe testRecipe +Decodex > [+] Added rotencode module to recipe testRecipe +Decodex > [+] Modules executed: hexencode, base64encode, rotencode 3 +[+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" +Decodex > [x] Unknown command, please enter a valid command +Decodex > Goodbye! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 039b5f7b53..554f26e80d 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -13,6 +13,8 @@ list select recipe testRecipe recipe new testRecipe list recipes +recipe list +recipe list testRecipe select recipe testRecipe recipe push hexencode recipe push base64encode From 69183e055d1397151d6feb24f5dc41db2440baf6 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 03:16:45 +0800 Subject: [PATCH 375/607] Fix stream indentation --- src/main/java/decodex/commands/recipe/RecipeListCommand.java | 4 +++- src/main/java/decodex/ui/Ui.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/commands/recipe/RecipeListCommand.java b/src/main/java/decodex/commands/recipe/RecipeListCommand.java index 2fb27dd8bf..8fb9d6ed4a 100644 --- a/src/main/java/decodex/commands/recipe/RecipeListCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeListCommand.java @@ -54,7 +54,9 @@ private String getModuleList(Recipe recipe) { return RegularMessages.RECIPE_EMPTY; } - String[] recipeModuleNames = recipeModuleList.stream().map(Module::toString).toArray(String[]::new); + String[] recipeModuleNames = recipeModuleList.stream() + .map(Module::toString) + .toArray(String[]::new); StringBuilder recipeListString = new StringBuilder(); for (int index = 1; index <= recipeModuleNames.length; index++) { diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 4cf8aac6d8..bb7efd5798 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -106,7 +106,9 @@ public void printRecipeModules(Recipe recipe) { } ArrayList recipeModuleList = recipe.getModuleList(); - String[] recipeModuleNames = recipeModuleList.stream().map(Module::toString).toArray(String[]::new); + String[] recipeModuleNames = recipeModuleList.stream() + .map(Module::toString) + .toArray(String[]::new); String message = RegularMessages.RECIPE_MODULES_EXECUTED + " " + String.join(", ", recipeModuleNames); printSuccess(message); From 6172c855c9387c37afad535c4d6aaa6444ec3f06 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 03:17:22 +0800 Subject: [PATCH 376/607] Fix CRLF to LF --- text-ui-test/EXPECTED.TXT | 56 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 83c38a4b5f..454728f5f4 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,17 +1,17 @@ ======================== Decodex =========================== Welcome to Decodex, transforming data with ease -============================================================ -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Your command cannot be blank -Decodex > [+] Input: "I am a lab rat" -Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "4920616d2061206c616220726174" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "L dp d ode udw" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Reverted all changes +============================================================ +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Unknown command, please enter a valid command +Decodex > [x] Your command cannot be blank +Decodex > [+] Input: "I am a lab rat" +Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "4920616d2061206c616220726174" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Output: "L dp d ode udw" +Decodex > [+] Output: "I am a lab rat" +Decodex > [+] Reverted all changes Decodex > Here is the list of supported modules: base64encode - Encodes the data using Base64 format. base64decode - Decodes the data using Base64 format. @@ -20,24 +20,24 @@ Decodex > Here is the list of supported modules: binencode - Encodes the data into binary. bindecode - Decodes the data from binary. rotencode - Rotates the alphabets in the input string by a specified amount - + You do not have any recipes - -Decodex > [x] The given recipe could not be found. -Decodex > [+] Recipe testRecipe created successfully + +Decodex > [x] The given recipe could not be found. +Decodex > [+] Recipe testRecipe created successfully Decodex > Here is the list of available recipes: testRecipe - + Decodex > Here is the list of modules in recipe testRecipe: -There are no modules in the recipe +There are no modules in the recipe Decodex > Here is the list of modules in recipe testRecipe: -There are no modules in the recipe -Decodex > [+] There are no modules in the recipe -[+] Output: "I am a lab rat" -Decodex > [+] Added hexencode module to recipe testRecipe -Decodex > [+] Added base64encode module to recipe testRecipe -Decodex > [+] Added rotencode module to recipe testRecipe -Decodex > [+] Modules executed: hexencode, base64encode, rotencode 3 -[+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" -Decodex > [x] Unknown command, please enter a valid command -Decodex > Goodbye! +There are no modules in the recipe +Decodex > [+] There are no modules in the recipe +[+] Output: "I am a lab rat" +Decodex > [+] Added hexencode module to recipe testRecipe +Decodex > [+] Added base64encode module to recipe testRecipe +Decodex > [+] Added rotencode module to recipe testRecipe +Decodex > [+] Modules executed: hexencode, base64encode, rotencode 3 +[+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" +Decodex > [x] Unknown command, please enter a valid command +Decodex > Goodbye! From d9be4a947dbe1cc9a07279edbaa37c3b57f88726 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 03:30:31 +0800 Subject: [PATCH 377/607] Update architecture diagram --- docs/images/architecture.png | Bin 16497 -> 0 bytes docs/images/dg/architecture.png | Bin 87577 -> 88592 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/images/architecture.png diff --git a/docs/images/architecture.png b/docs/images/architecture.png deleted file mode 100644 index d8b9c7710d99639b3d586b43442c3e85b7597755..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16497 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 RynzyTHMQc@Zf#h@H1K|<+rl!l=!kZzDx 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

BrfdFxYwGQ_%-y!h%<0TuFm^>A%fRVaX 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 Date: Wed, 27 Oct 2021 13:30:12 +0800 Subject: [PATCH 385/607] Fix typo --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 37014495dd..ae12a1cdc3 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -340,7 +340,7 @@ Modules that take in at least one parameter This group of modules require at least one additional parameter as their encoding and decoding processes are dependent on the provided parameters. The constructors of these modules should receive parameters as required and stored in private variables. These variables will then be used in their respective `run(Data data)` method. -The current implementation of the abstract `Module` class provides a strong foundation top be inherited by much more complex modules, and developed into full-functioning modules for Decodex. +The current implementation of the abstract `Module` class provides a strong foundation to be inherited by much more complex modules, and developed into full-functioning modules for Decodex. #### Implemented Modules: From 2a51bac75c2e586c974b8767e197e87079ed13de Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 27 Oct 2021 16:11:07 +0800 Subject: [PATCH 386/607] Claim classes and methods --- src/main/java/decodex/commands/HelpCommand.java | 1 + src/main/java/decodex/commands/InputCommand.java | 1 + src/main/java/decodex/commands/ResetCommand.java | 1 + src/main/java/decodex/commands/recipe/RecipePopCommand.java | 1 + src/main/java/decodex/commands/recipe/RecipePushCommand.java | 1 + src/main/java/decodex/commands/recipe/RecipeResetCommand.java | 1 + src/main/java/decodex/commands/recipe/RecipeSelectCommand.java | 1 + src/main/java/decodex/recipes/RecipeManager.java | 2 ++ src/test/java/decodex/commands/HelpCommandTest.java | 1 + src/test/java/decodex/commands/InputCommandTest.java | 1 + src/test/java/decodex/commands/RecipePopCommandTest.java | 1 + src/test/java/decodex/commands/RecipePushCommandTest.java | 1 + src/test/java/decodex/commands/RecipeResetCommandTest.java | 1 + src/test/java/decodex/commands/RecipeSelectCommandTest.java | 1 + src/test/java/decodex/commands/ResetCommandTest.java | 1 + src/test/java/decodex/recipes/RecipeManagerTest.java | 2 +- 16 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java index 572423b971..5ae0a2cbd3 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -12,6 +12,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; +// @@author rizemon public class HelpCommand extends Command { public static final String COMMAND_WORD = "help"; diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index 752fe5d7e4..6e2366db4b 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -8,6 +8,7 @@ import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; +// @@author rizemon public class InputCommand extends Command { public static final String COMMAND_WORD = "input"; diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java index b692ce7886..4e7127fdb2 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -6,6 +6,7 @@ import decodex.ui.Ui; import decodex.ui.messages.RegularMessages; +// @@author rizemon public class ResetCommand extends Command { public static final String COMMAND_WORD = "reset"; diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java index 68dce0e03f..950892a243 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -11,6 +11,7 @@ import decodex.ui.Ui; import java.io.IOException; +// @@author rizemon public class RecipePopCommand extends Command { public static final String COMMAND_WORD = "pop"; diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java index 00c9356950..7e6f43589b 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -14,6 +14,7 @@ import decodex.ui.messages.ErrorMessages; import java.io.IOException; +// @@author rizemon public class RecipePushCommand extends Command { public static final String COMMAND_WORD = "push"; diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java index 6cf6d7bb8e..2bf644df78 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -9,6 +9,7 @@ import decodex.ui.Ui; import java.io.IOException; +// @@author rizemon public class RecipeResetCommand extends Command { public static final String COMMAND_WORD = "reset"; diff --git a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java index 6a4f9b70be..3b050b583e 100644 --- a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java @@ -9,6 +9,7 @@ import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; +// @@author rizemon public class RecipeSelectCommand extends Command { public static final String COMMAND_WORD = "select"; diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index a0a3e7eb5b..e75d4728fb 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -102,6 +102,7 @@ public Recipe getRecipe(String name) throws RecipeManagerException { return recipeList.get(name); } + // @@author rizemon /** * Gets the Recipe object that is currently being edited. * @@ -164,4 +165,5 @@ public void resetEditedRecipe() throws RecipeManagerException, IOException { editingRecipe.reset(); storage.saveRecipeToFile(editingRecipe); } + // @@author } diff --git a/src/test/java/decodex/commands/HelpCommandTest.java b/src/test/java/decodex/commands/HelpCommandTest.java index b58f2dba75..676446bab3 100644 --- a/src/test/java/decodex/commands/HelpCommandTest.java +++ b/src/test/java/decodex/commands/HelpCommandTest.java @@ -15,6 +15,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; +// @@author rizemon class HelpCommandTest { private ByteArrayOutputStream outputStream; diff --git a/src/test/java/decodex/commands/InputCommandTest.java b/src/test/java/decodex/commands/InputCommandTest.java index 5fe32b3256..b6f0e4b455 100644 --- a/src/test/java/decodex/commands/InputCommandTest.java +++ b/src/test/java/decodex/commands/InputCommandTest.java @@ -16,6 +16,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; +// @@author rizemon public class InputCommandTest { @Test diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/commands/RecipePopCommandTest.java index 9339f83a34..fdc6de1ac1 100644 --- a/src/test/java/decodex/commands/RecipePopCommandTest.java +++ b/src/test/java/decodex/commands/RecipePopCommandTest.java @@ -18,6 +18,7 @@ import java.io.IOException; import org.junit.jupiter.api.Test; +// @@author rizemon class RecipePopCommandTest { @Test diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java index 1383ad47a9..6424de7152 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -18,6 +18,7 @@ import java.io.IOException; import org.junit.jupiter.api.Test; +// @@author rizemon class RecipePushCommandTest { @Test diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/commands/RecipeResetCommandTest.java index 239833f2bf..4557c07c60 100644 --- a/src/test/java/decodex/commands/RecipeResetCommandTest.java +++ b/src/test/java/decodex/commands/RecipeResetCommandTest.java @@ -18,6 +18,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; +// @@author rizemon class RecipeResetCommandTest { @Test diff --git a/src/test/java/decodex/commands/RecipeSelectCommandTest.java b/src/test/java/decodex/commands/RecipeSelectCommandTest.java index 9a03c5268b..1adf28b476 100644 --- a/src/test/java/decodex/commands/RecipeSelectCommandTest.java +++ b/src/test/java/decodex/commands/RecipeSelectCommandTest.java @@ -17,6 +17,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; +// @@author rizemon class RecipeSelectCommandTest { @Test diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index 7213a66025..cdd771cf46 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -15,6 +15,7 @@ import decodex.ui.Ui; +// @@author rizemon class ResetCommandTest { @Test diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index 13b1569e51..f36f302efd 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -8,11 +8,11 @@ import decodex.modules.Module; import decodex.modules.hex.HexEncoder; import decodex.storage.Storage; -import decodex.ui.Ui; import java.io.IOException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +// @@author rizemon class RecipeManagerTest { private final Storage storage = new Storage(); From 114358809d4f1868d15e1da098d7706b70cc98c9 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 27 Oct 2021 16:25:07 +0800 Subject: [PATCH 387/607] Update UG --- docs/UserGuide.md | 55 +++++++++++++++++++++++++++++++++---- docs/images/carbon(17).png | Bin 0 -> 19367 bytes 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 docs/images/carbon(17).png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 59adece06f..c87debb24b 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -74,9 +74,38 @@ This section serves to help the user better understand the terminologies used in > 🎮 Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` is using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! 6. For more information on Decodex's features, please refer to the Features Section. +## List of Available Modules +1. Base64 + 1. `base64encode` - Encodes the data using base64 format + 2. `base64decode` - Decodes the data using base64 format +2. Hexadecimal + 1. `hexencode` - Converts the input string to hexadecimal bytes + 2. `hexdecode` - Converts a hexadecimal string back into its raw value +3. Binary + 1. `binencode` - Encodes the data using binary format + 2. `bindecode` - Decodes the data using binary format +4. Rotational Cipher + 1. `rotencode` - Rotates alphabetical characters by a specified integer offset + +> :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. + ## Features -### Input of data: `input` +> :information_source: Arguments enclosed in `<>` are **mandatory** arguments while arguments enclosed in `{}` are **optional** arguments. For example, `select {moduleArgument}` would mean that `moduleName` is mandatory while `moduleArgument` is optional. + +> :information_source: Arguments must be entered in the **exact order and position** as specified in the commands' respective formats. + +> :information_source: All commands and arguments are **case-sensitive**. + +### Understanding the Application's Prompt + +![carbon(17).png](images/carbon(17).png) + +After running the program, it would display a prompt showing the name of the program `Decodex` , followed by the name of the recipe that is "currently being edited" (if any) in `[]`. This currently edited recipe will be the target for some `recipe` commands such as `recipe push`, `recipe pop` and `recipe reset`. + +### Basic Commands + +#### Input of data: `input` Stores the data to be processed by modules. @@ -95,7 +124,7 @@ Examples: > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. -### List available modules: `list` +####List available modules or recipes: `list` Shows a list of all available modules. @@ -103,7 +132,7 @@ Format: `list` ![carbon(8).png](images/carbon(8).png) -### Selection of module: `select` +#### Selection of module or recipe: `select` Selects a module and processes the data accordingly. Subsequent selection of modules will process the transformed data output from the previous module. @@ -120,18 +149,34 @@ Examples: > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. -### Resetting of data: `reset` +#### Resetting data: `reset` Resets the transformed data back to the original input. Format: `reset` -### Exiting the program: `exit` +#### Exiting the program: `exit` Exit the program. Format: `exit` +### Recipe Commands: `recipe` + +#### Create new recipe: `recipe new` + +#### Select recipe for editing: `recipe select` + +#### List modules in recipe: `recipe list` + +#### Add module to recipe: `recipe push` + +#### Remove module from recipe: `recipe pop` + +#### Clear all modules in recipe: `recipe reset` + +#### Delete recipe: `recipe delete` + ## Command Summary | Action | Command Format | Example Usage | diff --git a/docs/images/carbon(17).png b/docs/images/carbon(17).png new file mode 100644 index 0000000000000000000000000000000000000000..081f86acc1a2a309a1a99e2562994c0fffeb9d64 GIT binary patch literal 19367 zcmeIaXH-*Bv@ROJhDuRXq>CbFCGUP1s1AS$91>4e@1(whMlr5CA@ z8tEkl6bJzV6qt^i-iT#I!#9Es85CIig}H9N9UXe7BcCwk z^)PLy#=&eTIUD66Ao=s{gU_~SdZ`||h5Pu_S9lTX*zM`VS3a}!Mw+PdKhRF8JN?1d z##+QA3vr(7v+Fx^%CDVQ++|;RxugcXkH$e>K&bZDsn%UWn@#=ti%W)$NBanniqjW~ zcSnm2NBtE2LZ_hAVV*5}1tIuSo5a1npp=x9_vYnff*}y$2ab-8_MxHHI_c7!6|G0X z2HMvNcSs#_8=r0rC=e`H<&IB-=+%G=_ZEf1T(Dt^$*|N$5te@ zi2OUz*l&GGrg7t2G(*1UnSZAlD-fo9yfFs{A*(Eu$Gh~5#l*zCF|%z^T9|)hg_op) zNfEne{#_GZazmNo_|zxY{=c(Pf8m_e>J`+r>MM?`0pyFT*@xj@zdLw9@)Jr2f~7upeUouj=J=mHc0*G7ge4R6M@+kw;TcX2R# zgQG2zoi5#e)^~^&@f#+se=qEf2CQwCZj3ApA-CcCX%5;@(j3Qu7G?>#_$YYF#Hyl31|>oKV+B%XZM-IK$nA{Py(T-|V4!BGsNM zN9#SXMW~I^wbq%Cd!eNEyLUo&qbkW8i}jc=(;3)mc)CoZuH%mG`PTCU-Oh#9X14Sx z(thsd`+CKjcoMPFlp_dh=uP?_sf|cWoX(&!m~1WGo~I*IrJQJdT{*}NW4-rxK8&wb zaVH^Ucd9!2i^fRQfC4{h|GU$|&^#icBkCv1Vr5(WW-e)qqVaD=Q%InQqImSy?02Pv z3=@tx4ib)P^CvwlS6jZxqCj%3igaLCOj^13@QHU42iyBY5>7uwcI%yXNcs%VF*CS$ z6_kTQz-_;S*@hkH4g{Wt>XUYU>yvjxUd)mA4>SlpilOrM*|OXC#kCfol$?kl9X`!| z!Nb6T5irESP1jrhc#(c_%{}Efs?>%r+o@bS9{FMdb5`%~%!K-7r4)YoLC)^{4kadE z>4DxB>c&q;%E&)zQ9MdVlc((+Z9dP+6QTBN`BB~xP%6%DG)J8Ig|?XEQLc*QpW+dc zSXw}LWOlP23o(uuUV;~cR38D}Ra)5h?KwAWU@ ztX8c2{RDZOEKSXGdP@XcB0|B7Ycw6I=#z8sJNm}$2g@m8_69(7$|W+oAxmmO8~IeC zBB*KIerb)BmWF46O8c)K^kYu6Ft(rpr7*sPvnLtq7a={vWH#=-O zycx71h5*AT$@qM3GV*%R8aAK1885a+^$O^>n^lQ)Y3wIFZ_55yTfT|*F)1Z^36RBA zBB4uHk!bqo&$rz!Cyw~S^?{k5`C_GUfk`LFXHn4hofM{^HHPu1E?}mbJDfduIr2E48?N?19G1`N z_JFavKf&8*^^O)tqZWED)9USz=5JJC2dKeH2tLA^%wK(Mi&O)T-HOoW`Pm@v2JmY& z?J~#LEtJy3Gsory(FAlE5;o#XbrluCT-M@am($dC7@8MRNs&^~zhOdsoy(g#*NHez z@Xww5{YmoR%KYw3^9Wimruov#HtoBGHWj;~?aQX^SIGw<%(IpB#g$F2sQ_SVDHgun(;$~qCxQgT|LzlL)OP_ zXS>LDuM{@-vUj6LiDGR&R=XP}#P*I(DDs@H;-}pxZ~O*bC)m3*(-IxK@nO|-K=bhK z-LEHklw_|DV^R{MW1Tpxk9GgEG@pQS@#tgk{lVC-R92R>sf%7H)~+!3tIku9N9TNrFhKLcC}&(&GHBF9Xq&08+k7-lb4&G1MK8YHTHAD zzRO6QoII}*sr{)XXEzw1A8b+t{eIH$q$4@dw;s5kS#SUh-Z>J$@ge#yQxg?=SmpE~ zss!_L?pPd=s&RW4x@gn7t6d_a4_Ixshp#ML(A+XqrPh6OBMQ3M5k?IE{b^s1eE3r! z>}UTZ3H{_N!=t&uX;HtW?8&yEq2B1t+#6)qnzDat_Sk|Tl8I;Z9@m0{EqfiYodcQD z*U9=&uEk0Mmc2Kfq$t$7`}@&mpHe8-%Et_@(>wP&l@1m|$^K?@azn|@YHpX{8DbPl zJd9dg#zmbve5F&5`6+TrY4i2!u?7lqq2?O^X*-jgu$zhFO*I_y*L0^_E4Fc>iiBv5JsU)NoKStiVo$k?i;~Pu1X1m8wnkjE zQbwGW?#6qouogm{(@y6%gid0M)$zRFq-vi2x0FGQmBasqY`^|z0|NQ~2@`}rP-vp8 z2M$`wHjaHVWZv~RWkN7Y5R1hQ;rtDwI7>@Qhp>2iJG<=dLuCnZW*ReV>kkh~jrZ#y z4#jA!C1ZzW^>~7M?)x0c#<06@s?>UUp7G;r!sLujZr&au`G)_a0r&x+Nq;@I(&3xYp-b9Y)no zBXfS(c?yhQTU$F+zid6~CdtqiZ(U)szcYPKHbZBYW-%BwLz>~TtZ4hOoa_n7R(&lB z3w@FS(y2SlX^@*eG+C8(b=>3$R8@(NxQkQ7zQ44&G9&DeghhFHdFfwaWL!-&5;23N z8tXStfAFZcScPj$%5g4;tmA`+JnD(dwkYc)z^s+JH_f*c-lX;w02-anRx-y#mLZXv z^^-IgpLreMkLfie-@1a(98icVmYja~I?&sJ`2@1C5X?G1;Pt7J-gH%*fAh9`SC3_L zB-<~?L}+@yW>n|-LFSem*wvJISJ*pXuzGhMUC6RW?YY*PN6h5iZAL&Pj;bPWe@5Bb z+1a@*DY=LZir|uIzZ$*# z%DEv~-KKsLcbcoGr>AoAyM3Utr~+Dld@>HrBOfi)6rh5#t_Ud-*XdrdGs0V&aNfFg z>t5+-+~}yWnT<{CA?h>)lIxU5l^`GAdPQO*+Kw6SI2B;<{9C|Lym~GtS&D~0_zf=& z(ht+LIzJkF3m-8eBUChS^dRDpKz^766`>-F*)t!R~zQnU{<0%(7RZ*4~M`r=Gxg)0n3?bb(7iPusod@-unLrT)z>~K$c(J$`ez}Z zldq0LzEAxuL_L*Ib+MEh%3h7kX~~Xh>6aiyd;B2VAK{C<-3Sfh`i>T%Oe7}OqkfW( z&r)i9sXYV^w*tcuY5qD^%RCwOXf7U$Nn2S?hQ0u8qo*brwm{o{wY(4xqAbrebvj9)udFHbsrrxUHa9M(Qmw9oN;UB7XJD+7!o;LptI^d zh*#wOIqFat7KN|m-Dd}j@TEswRF@R?TvOHUQPvw$S6^A8RHgQFNaZX~k$bwnR9_@0 z-1WCqlOxrp@?x7VTqIb-Z?K`SjxD~gX$?NfZJ|8mCTTc&8U7~3;~5?c)^VxexE-ik z%8M*)syoSD;jZt`cj8mZVX?Z`;{1*n6vPEx((Wr%NetbOWwn?uvaW!QrpRw0UKBxP z!@c0`!plm3R~Hu^|5&4!SlwNH*K3USRd{{QraCgPW&5b%>vo>c?bE^WZib zaWB$ITmycxtL9m4?;8e25bu3GE-R1=JUsoKCwYRn!}@%NlcLx~?RPklw{6o%YEeop zx~#8%N=**Wu9*7V$4n8o+wU>I%3`VEgxTl8zn0h_YS{MM8Ihvw5@*Q zgQFdCpy%;S?ePvr4Dh~>3i**4nHP~;LdeYT_Nt73ZcjIa#k1*d`$t00 z=%`He27|AazgNuVVPW?6mI4~O5bFvSiNQl!WA1Dj0>2K+?l#dm40nf%wPGr~0RWks1%UJk#`o^*SP zU>|cPz5(l2yWJ7PB=&wvg9;3hSl3;o1cBH#sBQ@W} zCl8=4x~8tRsV4O}y(-#h&FZ}T@-M7Sca<%J3jgtR!!A+do<^y5a+f%KEn5g7b00Xg zILA?z8y=VtmQ}Ml{=WEKdi*yjdx2o~(`^3v)QP{RKqrR9_fR_|*Nz5{4sfIsw;k^^< zry!e58F>}m6(*Az&W_RMujTX(1}~f|5*mU(I3lBO;kobIUU?`;d1xd%^0y0!al?Nl z7zjx}1(;(`ovZZ+nV{~K%Bv4)`7xQ2fT@<;b=U}NPF@{xQ&2^n&)=kYwf^P(Qh^p` zAz_b;XEoZQ=Dmx@`kcoEoaL!vDb))_K^KHIce!!`p1370ZrO2bp&?iSdas5(iZ4EA zB{6fZPKuc}@}0v`yg5!s?>%()4BHt8_(hJK%!UWgMAd=%W3b`411&y;=u(g9?A=V) zhV_$nFg75eq-fmK4`TN7_}W?t5Jfuv1V!dY$RP^Nv9$4Ely#>;g-e}Mu;FF44Ns4$ zxt(FCvPQL<)0v>A#;Cxrr7Sv}gn+B_Yk1KnjhHr3L~!9^@K=R5>dqhbbRX9Ww~2(P zp0)|f{I*;UZ6O+L3x6ovLhPR4*+1}MYZYBqqQ)O8f;;(aTW6B;9yBw2OO)UOm4<|h zDhkcaE}OplI^$YrxckB$uBg}I3dVi;bsj%ioss19(19cWO8G%Xc= zEI-i)zKRfehO;M?^le!RL{o3-yH5e%5Z2dZJ|N812NXL1y%4z{jh~n>Z>>Z-i-Yxg zyj1_P!O*ZgCNkDT*42%J>59}ZOR0%ql|JB&j{bUxGm`_<#k2x;dtj7nmd(@a zNvfrz%x84nVz%`h;-c8=E?8AHjJwf}NTBu3SPAa`9Krgz)S4fL5oq*#vG@bZrD{u> z`|x!&C|sPlARFLYlocC0erjt&ajSY|E6Bki-{x(>%!J|%;A*y{ZV3v4Oe5((m*<(! zttLT742F*T2g81Czhiab5_6Q#u1DFgP!H##84Q(rMKmV_*-orrUW0hy<2GypgZ`w@ z(_6U$eA{waalMOTY1y8xn-3y+lUAWv1_CMipm~aC>C{IwHoqxo)T*{rI64j2pcMd$kfYXHM~r;InNw%T@5#mdJ{>4=td*IsF@(sUWB!HM4~Av!$;WLY}%? zB-(YuWqhv4qaJ%cf0I3pXIhoB`!e5N*|&MwFco%=>1M&=3zEIKADr4#0h13-uza`$ z1Qm=esi?zTFgfH2w!V%?40I#rsU}OyTI%fg4Wc?jcGEj#e@4e|%Q0=rW`F<~XoNgT zQgS7m67pHP)Dn0re3ZP@Z1(t)l1L5>6Z%L{NQiFg6A01FtgHkUPGM@r-6v**z;FLB z5C)gL%Nlf^s{MxKWi)xY#Bk-O$ERFfd%swQ;j8z>ifzFZVChLoNgHQhfKb!`UEA;| ze$nTRe{)9T5mr5y`gGH@N9$?zvH9wjmKh~Qm$sSF(b1=}KXLvJ4i4X$bRvqNgH^x% zn-2*SV1ALnnw*J<%70TsDIlXFWjCjQtM)VEQ|@Ydk=y9< zmx+(!g~85?cu4rHC<+}CT(_1T=`ipGq-2q~1{AMWXVUzKS|TDMo(irgUkGU*rGls@ z4J=gEPT0~-RGC0P5v|Rz7;^IJ_3JaBfwX12Y8wFVYG%@N>XWPh80m&og$_dw&k68%Ovx;BG~~N8+vF&n zJ`#nxOWUuL(9#F0N}>}K-FsWhi;GD%A5^1g6Riu5PY=9tIcwyxL{PTac-S=Nz<3i2 zDu4mV=9)VouhI$NDDKKc7=g4;O# zI%Kh##IaGr_+GxPLWtV*GlGhrln z5b2PxxVhNly}8hp9R?+na}D+W94JAWE4^%}N@Tsd(jJnI?$-%&A9i&rEJJL4iBZ^8 zfy#sgW{b(tKTz8{f42(P(^oot_@wP})&g(*^J<2}!aZsS9!^irGs++E?#Sd#T}JcuvrQjwcPm`r%8wra#f^u;-8&kyYgxCq4&Ufi;W8mAG!L_L$Y-}jRUgZqKSftOb(EdyC8f!4Zr}wM(4VED{wVI#@fsjpr@RfmX70K)#a~fwo{tDXA3uD`b9Y ziu+T%@_aP&Ne~$jO)Kbz(skQ@$7CUNnaxAZrV#e!CwGwFNgmE_E{`-%>kRgvM>&z} zSLa#D`jvt}ByBAZ(4EJw`Zwo$9An{ArR{>sBB&ddoZ$k#KM*VO!lM7#3xM$dp1HCd z_M~m*`S&`ZeK9xAH~h-f`rP{5ZhsXV+l1FCG+W=YDhVbD*|2xF99!$b8~gAan4_BN z=}A5@FG~u|?JfAKLzsI-b%aCpYzHSv{4+Xm>LQRK^{e zMW!UkBAEQ4#>2j&-hlze&1o`m5T!&qJzxQyT@3M5TbX1XSGctVoEcTj;rxJZH@}ch z7qH092{U3<3-_hnU+wHcxZg%3xjxC1a4whPM{Msb$D69zPCG#tBKj9T>*eMBQi-~< zFP3_jdmrT$68U36fPwAnH+bv}Iwx}TjcT~}!S3Iu8V{SNJvbdeWCnrUD+Gdbjkt=i z;|VU(jRP#K3LX109vyn`;UjEz4(#Q(#wrK8=fLKGcj`80N6P4_Bt)|}#gDY9g@s;F z4Ql5kLrI&PJwjp{x5y(JO@Yk8q^@8wk+CrSwyF{Kry!Z=FhzzfvnHwNSVq};*ym*S ztFjG$n2#6W^_n5Czo-qI3?)E{a0e4GV3fV=Jb;s`n=1U0v zbKqI~e3!EzF8`@}D&f!GJ5VM_w`9Mf)|&f|z2_+}y}9*o8Bo?g^A8(@u>Q{m77XQDuMyq+pWpDDrQJZAgq&xB)1uf}$S?GuMPcR&5Mwa-ce#(87^h0xGm~b2%U& z`_LlrnP+y2A{=ITj3Vgo``ZRyxpKvzR;$EU_v6Qp*FfDFl*P0#SF+9mrwn{_Rb?d^ zlqjA-d(=BTdZaprMz2Q1wM|TWV$ zYlcpDK+)!A)yU(3wL%X|%G!zDbU9EkZ{=7b-FdEdji2quo!UvuJ9m#<#--EcmI8fc z*@{O!&=}`;XUN!&l~tlgeHxaagRan5^el0)C#jbZ&jO3%LMN*u{!uxY$uJ`8uM7|n~) zh-E&RUp&&E73(|SyE5H?L}MQfw>e)*5K(hYUuT2A3I6eVP;3({fcwb>gCox3`%30_ z{0wU(C}SEL#e~i0o>3}LCwVILF-RNfXF{JYS=MGSEUBmbsbuw{NxU9)_o45LrpCex z(ya5Bw2e`mX6o~@J%?wi_=8`LHB8HKlug8H+6t-)HYzR{#9F)a6M^Wq0uPZk@t;xS?IU%bLa@D5%Wd zPFNqeKSBiMEC-)0AWVNR?2qD!2gy_O+t+4%QxEQL$2JW7NXrW@^%Qm|mcg=rS~Hep zlGVV>jA=I=pmVFfvi*<5-Y&llstA2$K6c-744c|=#GRO-`y3j~j@@`;T_4|l_w=*D z_A(|65_YR8v}bN+-tcaYpMVDk$U4h9q??2!Tl@rjgc;y5PY!ibXHhQD{DJEpmlLcj zmi+{xLv?0g&&&nFgcEC#dkHw*WAUH5 z)s*guY-sTn%1~%K0pG0?kQrI?gnvtX;V)SwCdebQ` z-n9dY$F&z|_15sgq=!j$v$-tQck(w{Vw4Z(<9t;o+$75xwJ~Y| z#F8|y4s6}-?^stGH&m#~Fwx-%vFBCeH+Y{xdUwhQ!~=(I8xtQIPHAteceRi0R@1HFllLdoWxYee;Ezwmsa7W>bU-`3#4a|bs z>(tvp&6V#%kK#Z1^#uUvDt5Bvou7bOx2XCs8p2^kRq27-#V^G}Ywto~2gr90l3;KCWH4 zAE6TC7XDDs)P<#ee?idOKoz5&)o9CQCGLmr(J35mZ4}eB>}zcdy^23+6ZUqGwwlc) zQ#l`XLAXLnX$ko1!$%iUl{Hq*_>*CvDnw#ha*5ol*T;4z7h=6$ii#|HayyPnl@^%1 zlJP*|n8psEk=FXxijN|A;+=1phUtK^k$x}~M=u$s#(`CLzaZ8J+DcDm+b_A<+?K!Mv(ZS#7S7tE zH!E1b8(c{CHI}}HEgeLX28zZqMjnMw#Mk!%#DOxQh9Reg@RIcXM7o(!4 zC7)^UM8K#kIhKBq`0c}{_lmtuOJWCK{B&_mN~4Q7L-ZUw<;r|oLBHdLIf5<#nFuby zKOdyxv|zfnFV+>7hxx_U8zzt^moV{PIjyATW^CF&-Bup?2Qvxemgc=5IU}x{x=rUY z6kEQRUFqI>H29Vn0H38Emk`C>FWiV%-b!uwrGY+)DExhgcD{?>^%s4K_I7+cJrMv% zS?()oW{sMJiC4{)=Zv|Vp0az#nfGTLs92&^K!sP$B=S|u%<{q8w|8U5JPir+q37UB ze|}hX%&!?t4UL##bBhBwJ=Uvf#r`sBx_0~i+{#Va5*lc{JePTmPfPf64ssNW4}B!| z4bQD>)nkEdA&T;W>R(iBi-Jc8*344b4w=y=ds_A2Xzysr-t4Bh?n{>7@%0*9LEa-~ zLTZEe@N%NN>6-BR_i%A_LFF!4|KS6@%OaeX2`xV+wu4dMID!#{QUEN7kXpy|eHZqv zLP+NCz)?6AEYZF%#AKA^sTc8sHL8b zkq+2A=Vf(HgrqSg`Ca0dHe;yC4dWnKW ztyc<%+FEplC+%RA>lPot${uHpWzmj6y1J-V{vbP-HJ`6LOlHjJAcw}Nos1B(*8cj; z@6+=&lenj|!r2kKhhiQ5cqX$CMLIBD`g+f7Zp?ijj^rIqwJE>PWb7LE)Q+hbEZV)Z z4gmyj3CcdTcXb1Qg035sjtlMXt3$T~C(C@Ke_B_>TKK~JC~*^vTUNO>w|qZ*=Cn^) zDTFL_XvbdAx$*4)ssV~}>HO=UQ5M5_-|Wfy0>fI9B&Y~)iof+0PAItG9LjGc^{eG+ zvu?`n3pWRV04WH1`At{`M9Fa6*}l3Oxd0;2fLxJp3b%(`hff%0uch)5_3Tu}$ZFl! z($FFYq76DYPIt@VH}Q`$ofn?BS#-S%2z3?1I;mhjR@YH95Eci0B1QCQwqE6&yfen8 z%#s4RGXRU@P8_r1eZBww)vM+&BQ!>6|NIoY$(O|JEjVA?orzO#2C(k@+<9iR-+6*( z(#on%(PsB>x%dZ*@tS_XofQF4dSFB7h-zZ=#&E$cAVz9Gt1VtMC@?R$T+1tGQBO_0 z=Z2RBk~^N)+{BDL%DhU`{BO=AF1X@i!t#G=>1w27k?Z{rPtH*&bv!oz%?mmI{_g`qr=(1^4w)AnOzB`v)Lq#U-K44*9-K z=_{YP0s3EXNQ;HQC9XOaZ47j^WnR-N(r=b&jFq-d5T!VMpl&?&Is2t(^p57=#&H^8 z9^07hi#=_E@pg)TfBkP-sn@F`g|pkEV&@N_`#)Fq=bH1jKLWddiXrB~&->}VGx7{k zeWT|mu${MPhBbfbT^oV+B3j~s+oW2%<1L3L5vZTbcz|zpHg48KAG*6=y z=HtjpeyuU&{Xk<|)Ym~r)V}%4wpG=DrS_&|VC3i-)^=7}WJ*IH-A`%%*&b%;OUB%? zyDUqFP8KG*n47Mu30=a_Tl4=#=y{mV2k9ox_(IDLFR*gBkD-f7wrfObrt=>cO^*YBV@Rb6&>*mO~E*c{AFJHrnpsJ8J*Ugh8@#k&T_zlE6U1M2KvkH_Pm zS`Uyrz*c>e#jhmluR?CR8dz1JiHuuSEYwIAZIpV5f%2ub(;dq>h-Dwu{#o0%HbXRe zDUY0^cy)j28Kvwh+Lexe9SFNG=z%o-p_BSs`pWUj<$k1QxIp8a{Muh=_(o04m~o>BA;ee)(j6Bb#(4=io z=h1(-pvm`93zU-LQ{%DlYY9N>kwgkOEu#cUV2I{&z5{o6ch9OMC)J?us;Z?WbB)?)Oonxv z=Bi=5`ngZo*~AE5BvpEe@zkh2{4QqZgnv(dD7 zufzw85-@6kIQ4A637V)?6rA%s8bpIaEIyXKt%X5+{f!LjGaz5k!B&pOsOQ>TabcGC zlFD>pS@!a~(PKF8tRR9PJ%9~u7_5*H+%Fo#c!J=qTY;pdm8MeYRX54B85&WPX94Rg zQwLBnAXG4bt{UXc*Y{-_)=&33MKnNvY|GdqJ(sH82nIpq-ia-y!9$UbIy5$jPN>@) zt&&ixDCBDfg!OdMq7{^Azw6laN{z*pMWFfS**k51&sVojj5_)60+jHU z5Q3>QKGfEVv3?~&a9ymoZv+6czb`f_(tM^T8aV8C$h^f}UFMC?b9gwv6jqAcJlSSg@KbTedEOI4z{E?@H)Gxog``guI^s=nuS%lZXI*y zaqR+z4KdJdh_)m?s^jd5Ljng5vdyu<6+9PK!Q+A1veIBqE8Gz=q|L=qSeE%VD2$}d zHi#cNKOly_YLP`x4g`BpxgI`yFH5W2&P>g(yH_((&>pMVR*fz41~Mx)XEl`0&NUy=yp_r3VEKP<+#!=I@nGfk!JQs0a6 zIo;WQejIq0!^@(eWC8hnt(wokmwFzgUKNS7PjfD00sOHN)QeVX0c`s0`hVl&dtW+b z;rlh8NJqp69@5->t=A}C6BD}&PZ9e{SW-6lhxPo(~@z-fNB zUDwo9Iw<=YZJ3#x$DLyo>?}4c)WjV1_1$lLGW)EzuP+f4EQ^xU(&EVnmzEq?Cojg) z8h6hC7%E(pKI_fXmjkxwS_JMeM?mt2S6}XJ1s#H+r<(jqNtrdXTK5z?!CP-XsH@lS68<%QS0ix)SEKp5VF+E6#3`G2x#?$!C-E|+xF$IO#z16o=#48 z>+0(re0+TDvD1KhHxG}u#(@Tch&y^26z9VO31w%>$v~+7KNtul4p7(?VEFK ztO+OqPB2;>XOnt=8?W!7?_v?O%=k)+84f{pn2n#hJbda4e9aUV!Bc5!fJ*tOqQWko*Uw zWB}y|d$d#$XJb-2z1|2QaXAH(V-!75;?R<>1A$18vSvF-=eHQ%1nAe0{2Z+pfQlAs zs|~$zI0dQ=%+$vyC!$;lNj-8gH^Z9YgrMy-jM`o>0Hfb#JzzyQ1(2LMKc($W3f-xA9gJ}XNt z&6A-n*EDN6Ifuh^vSBdFcFnet(W#HuyPc%t+--p5*P~dFkej+G0N7sHxI0B4e{&?| z1b;Q+Bx^pibCw!On~vH-wXzfP9UoMugxLH+(G9pHaqv-M!E!?a<2C#BWCe9VF3prh zy~kyPyH*aVb#JWjxP3gf|5P0^UV}_{CBJe+iE9hRD{ETmRSBp5^h%E?&yC9nZ;i7_ zDyMns;F~h-0KzU=fdDMtx*ws0A@v7f;3lA!%88+YK0TZ}?w7Zhv8^(7{u^ zT6!;sk_ZXbSlJjvfW871oy_Efdci5$OL4)TE1Y)$bVsa^la>>qF9U#N#!rU)Ap3{c zZi(2%?46%3{eH#r1y6iDbUj2}TwE04wY64bQjtzyLZ7u_4cTA)1Dww(=CJFlk!}m) zb@W7sJT~u#%yB^UNkC+ce%P_kkNFC9GS*H*z&_mrR)$RAV6^(vWro8@WGyc~01oC8 zqP;`npeH$9e1M*vEpx?M9vC#)@{d}_gXdQw@%dUwr&Qwd^KXt%P5tJOLR9scqeFVW zq}yot-Pwp;#T%DSn{{;`GzKrP{80<*pPHKb+?ZZKhz!!5fP5c0Tsc~q;i;QZ0F2Wy z^EvLJ5VUETBCWq>6w)jm<@03$-U8HKY4m)blXkbB(*#W$UN62I;qe=Kl2P_n;Ntg% zOs2Y-8{D=UsTKKtZb?1}zn2UID@6q*&^A1zxcaW}WwxC9IK?ZEA}$Ga-`v6?_nu}O z^D)Ss1^?X(=+|G)=WFb1TOl<#qg?EpBDuc1Cym$Tthr=vj%YuZFFyTjrX$aL7-2_5 zHq2kC0#)YcGl+%(L|Pg)N?k*Nub1$XGTX3F2jAN901?u|Wj^LYb!qY_S(&%^D3ULp z%R%QlY~@Gxkpw1iZ!zhY=ukNXa_#~7y#P#Opy~T8sNem+=w=X*Q~sNR;KXuO1=R^` zgt`P#CgiDqqP;h_lqj4XU;Z9(^W_hptb|%|4=`WdqQ}^icHM%-e}A)Noo`0wn?*o* zg(ra6!XiNv1tMPjpgkelH8ZiM>J(2M?n%UJAQX+u4`xA=14lB;f3~y?)d946$^yV? zf8Bf2(BJWbgI^-b3DGg^qK~loIc+g3SYM&5lUx2Dxo-j3oNCf$jYe*5 zH6c`Ix0)KZMhNo#*ImOO!ceqBbKUZ4DL;XUi-1_?6LGy6^mMGmfaM|5{Ws;MVY#oD zDSxV*KHPIL`5hg4bXw}Sf~gxZN2%?~*9OW2dWVY+U$0;uNuGB&#Up#-W8Y2X@P}dd zEyG32*m;cK`8C74zZD+f>FM52wn+D+U$BaN=j{F#`7aD0cXr&r&T{?M z$CKhMRVR4r*m)Ee^+87o!E*$bMW4mRe*@4QX97yP>T&+GQB}r(pS^1eU_D_ry|4K# zEaC&mEQ?vUNdYIBT}X3ZDHWdnE;`BM<-ne!?N)LrD_uUw+xE-==)hw73iLVB1Nb-# z=?fXZ#9z}Z=#@ynfEsmXQ*l>Q>b>t_WHF}JyZSZWBMeTN(Ae1cG>dCp$R%fluSf0$ z)cv_s46q}b6TFH~K^g6X5iM{u{rRtH#4K;>DsU{a^VD(5bM_xf8=uijRs#W3g3AKs zB^1|2VtF&wb9(--E0=w%B5CWo?BcB%X|8Rn)zn>sj&M~e`tG&;Mz4pWTz zV+Z23iC^#X8}gB^5~Q0jt4Ik%zc85QfR>h)DnD>1CwV$zIg)ykgb1y~J}FEB`SCDG z;37vJ-?);s!p@}eMZG3UZ72XVtW`x)?>D<;hnELLukLx_aJ#>9j{ReW+kMPsQ!aNW zgYb(b+HO63Slq40Ji5R|T3?hDJYx!0Ow+%4MebrWU+GmXyECaAU@*CRs(N9Cow!Eh zNHeMU6>9JJXHU5bZnsod$D48kK~>VY9v;?hj4m!15~{`|Oi)7#s&SMjQ8_<$cKqi8 z1Ox=kut+M%yldGiYACwPE$Sb(YSDRWo`nHN9hXCu02-6JKuk=M!@vnvATNjr zkpewO>D{M!$W1j1(Opl2bir!B!+vFJEMoO(`w{2@BX_neERDJZ;mY7L zAwvl%!!b=;Ij&};lJ*|bZF>AI2%a`ooV;y$3=E!RtDZY#>bt>kI3um7>b&_On$~Wv z7zG_f6t=_gUgTi=mO8VAVxkdg7o+huR#zq-o3M?S4g#A1afNc6J;^g@Fd#xnF zpeTB;o~6jlt*=oYzpPtB;aS-sYa_g_u`JW)9Gsk-Y`v%dt&9oeB{%di*lpgF5ZUCr zI`sL7Lm{}v#;FgXJMLKSs)*NY<#wsEot~cqku?+x*V0y_YImh=;Yx5Td~6R0 zqSIylN;o3(WCDPTzFuDeB_{$L&vgpLKE~AHKt5drq(GQ1>3It`s&o6xz(xm+aENU))UgXgnYSA ze^ zt!s(Lf)@Ip`LE%vN@`YHxbZw#UqfV`8etX0mQ*N>kaQUz?wy8}nQKT{+?Er6yek?B z(#R<*iib5)(YkEl2{!Zb`Gqmvst<6{4~c{KzYzkBfY8~ylWxrJo&5>Jo8-#jgkB-b_@=5T@;(g znRSsOXwXWmLZJk%2XKqU^ecd;V;8I~XgMPw$E$0a;? Date: Wed, 27 Oct 2021 20:16:13 +0800 Subject: [PATCH 388/607] Update UG * Update introduction * Add table of contents * Update terminologies table * Add symbols table * Update quick start * Add FAQ --- docs/UserGuide.md | 116 ++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 51 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c87debb24b..d6f9474795 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,45 +2,46 @@ ## Introduction -Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly transform data from one format to another with extreme ease**. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. +Decodex is a **Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly process data from one encoding format to another with extreme ease**. It also allows you to build recipes, sequences of data processing instructions, to speed up repetitive tasks. The intuitive interaction can help speed up a player's performance during CTFs and save time without having to manually code the tedious data transformations. -This guides serves to help you understand the usage of the program for quick and easy data manipulation. +This guide serves to help you understand the usage of the program to encode and decode data, as well as create recipes to automate multiple encoding or decoding processes in sequence. > :information_source: This user guide is tailored for CTF players who have basic understanding of information security concepts. ![carbon(6).png](images/carbon(6).png) ## Table of Contents -- [Quick Start](#quick-start) +- [Introduction](#introduction) - [Terminologies](#terminologies) +- [Symbols](#symbols) +- [Quick Start](#quick-start) - [List of Available Modules](#list-of-available-modules) - [Features](#features) - - [Understanding the Application's Prompt](#understanding-the-application-prompt) + - [Understanding the Application's Prompt](#understanding-the-applications-prompt) - [Basic Commands](#basic-commands) - - [Input of data: `input`](#input-of-data-input) - - [List available modules: `list`](#list-available-modules-list) - - [Selection of module: `select`](#selection-of-module-select) - - [Resetting of data: `reset`](#resetting-of-data-reset) - - [Exiting the program: `exit`](#exiting-the-program-exit) - - [Recipe Commands](#recipe-commands) - - [Create new recipe: `recipe`](#create-new-recipe) - - [Select recipe for editing: `recipe select`](#selecting-recipe-for-editing) - - [List modules in recipe: `recipe list`](#list-modules-in-recipe) - - [Add module to recipe: `recipe push`](#add-module-to-recipe) - - [Remove module from recipe: `recipe pop`](#remove-module-from-recipe) - - [Clear all modules in recipe: `recipe reset`](#clear-all-modules-in-recipe) - - [Delete recipe: `recipe delete`](#delete-recipe) - - [Saving Recipes to file `[coming in v2.0]`]() - - [Reading/writing data from/to file `[coming in v2.0]`]() + - [Input of Data: `input`](#input-of-data-input) + - [List Available Modules or Recipes: `list`](#list-available-modules-or-recipes-list) + - [Selecting a Module or Recipe: `select`](#selecting-a-module-or-recipe-select) + - [Resetting Data: `reset`](#resetting-data-reset) + - [Exiting the Program: `exit`](#exiting-the-program-exit) + - [Recipe Commands: `recipe`](#recipe-commands-recipe) + - [Create a New Recipe: `recipe new`](#create-a-new-recipe-recipe-new) + - [Select a Recipe for Editing: `recipe select`](#select-a-recipe-for-editing-recipe-select) + - [List Modules in a Recipe: `recipe list`](#list-modules-in-a-recipe-recipe-list) + - [Add a Module to a Recipe: `recipe push`](#add-a-module-to-a-recipe-recipe-push) + - [Remove a Module from a Recipe: `recipe pop`](#remove-a-module-from-a-recipe-recipe-pop) + - [Clear All Modules in a Recipe: `recipe reset`](#clear-all-modules-in-a-recipe-recipe-reset) + - [Delete a Recipe: `recipe delete`](#delete-a-recipe-recipe-delete) + - [Saving Recipe to File](#saving-recipe-to-file) - [Command Summary](#command-summary) -- [FAQ]() +- [FAQ](#faq) ## Terminologies -This section serves to help the user better understand the terminologies used in this user guide. +This section serves to help you better understand the terminologies used in this user guide. -| Terminology | Definition | -|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Data transformation | The conversion of one data format to another. | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Application, Program | Refers to the Decodex program. This two terms are used interchangeably in this User Guide. | | Encoding | Convert a message into a coded form. | | Decoding | Convert a coded message into an intelligible form. | @@ -49,29 +50,35 @@ This section serves to help the user better understand the terminologies used in | Argument | The additional information you provide to the program's command. | | Module | A self-contained set of instructions to process your data into another form. | | Recipe | Acts as a container for you to select your modules. When multiple modules are selected, this forms a "module chain". By default, you do not have any recipes. | -| 💡 | Represents a good tip for you. | -| ❗ | Represents something important that you should take note of. | -| ℹ | Represents additional information regarding commands/features for you to better understand how to use it. | -| 🎮 | Represents something optional that you can try out, mostly for exploring or fun. | + +## Symbols + +| Symbol | Definition | +| -------------------- | --------------------------------------------------------------------------------------------------------- | +| :bulb: | Represents a good tip for you. | +| :exclamation: | Represents something important that you should take note of. | +| :information_source: | Represents additional information regarding commands/features for you to better understand how to use it. | +| :video_game: | Represents something optional that you can try out, mostly for exploring or fun. | + ## Quick Start -1. Ensure you have Java version 11 or above installed on your computer. - 1. If you haven't, you may download it [here](https://www.oracle.com/java/technologies/downloads/#java11-linux) - > ❗ You should download the installation relative to your Operating System. -2. Next, download the latest `decodex.jar` [here](https://github.com/AY2122S1-CS2113T-T10-3/tp/releases) +1. Ensure you have Java version `11` or above installed on your computer. + 1. If you haven't, you may download it [here](https://www.oracle.com/java/technologies/downloads/#java11-linux). + > :exclamation: You should download the installation relative to your Operating System. +2. Next, download the latest `decodex.jar` [here](https://github.com/AY2122S1-CS2113T-T10-3/tp/releases). 1. Simply click on the `decodex.jar` under "Assets" and the download should start. 3. After downloading, you can open up `command prompt`. - > 💡To open command prompt, press `win + r` at the same time, then type and enter `cmd`. -4. Then, you can now run the `decodex.jar` by typing in `java -jar decodex.jar` and the Decodex's prompt should appear. Also, please ensure that you are at the directory where `decodex.jar` is. - 1. In the screenshot below, the `decodex.jar` is located in the `Downloads` folder. + > :bulb: To open command prompt, press `win + r` at the same time, then type and enter `cmd`. +4. Afterwards, you can run `decodex.jar` by typing in `java -jar decodex.jar` and Decodex's prompt should appear. Please also ensure that you are in the same directory as where you have downloaded `decodex.jar`. + 1. In the screenshot below, `decodex.jar` is located in the `Downloads` folder. ![carbon(23).png](images/carbon(23).png) -5. Here on, you can try out some of the basic commands below: - 1. `input I am groot`: Inputs into the program the data ("I am groot"). +5. You can try out some of the basic commands below: + 1. `input I am groot`: Inputs the text data `I am groot` into the program. 2. `list`: Lists all available modules and recipes that you can use. 3. `select module base64encode`: Selects and runs the base64-encoding module on the data. 4. `reset`: Resets the changes made to data - resetting to its original data. 5. `exit`: Exits the application. - > 🎮 Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` is using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! + > :video_game: Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` is using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! 6. For more information on Decodex's features, please refer to the Features Section. ## List of Available Modules @@ -105,7 +112,7 @@ After running the program, it would display a prompt showing the name of the pro ### Basic Commands -#### Input of data: `input` +#### Input of Data: `input` Stores the data to be processed by modules. @@ -116,7 +123,7 @@ Format: `input ` Examples: - `input SGVsbG9Xb3JsZA==` Enter base64-encoded data. -- input  hello world Enter plain text as data. +- input  hello world Enter plain text as data. ![carbon(1).png](images/carbon(1).png) @@ -124,7 +131,7 @@ Examples: > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. -####List available modules or recipes: `list` +#### List Available Modules or Recipes: `list` Shows a list of all available modules. @@ -132,7 +139,7 @@ Format: `list` ![carbon(8).png](images/carbon(8).png) -#### Selection of module or recipe: `select` +#### Selecting a Module or Recipe: `select` Selects a module and processes the data accordingly. Subsequent selection of modules will process the transformed data output from the previous module. @@ -149,13 +156,13 @@ Examples: > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. -#### Resetting data: `reset` +#### Resetting Data: `reset` Resets the transformed data back to the original input. Format: `reset` -#### Exiting the program: `exit` +#### Exiting the Program: `exit` Exit the program. @@ -163,19 +170,21 @@ Format: `exit` ### Recipe Commands: `recipe` -#### Create new recipe: `recipe new` +#### Create a New Recipe: `recipe new` + +#### Select a Recipe for Editing: `recipe select` -#### Select recipe for editing: `recipe select` +#### List Modules in a Recipe: `recipe list` -#### List modules in recipe: `recipe list` +#### Add a Module to a Recipe: `recipe push` -#### Add module to recipe: `recipe push` +#### Remove a Module from a Recipe: `recipe pop` -#### Remove module from recipe: `recipe pop` +#### Clear All Modules in a Recipe: `recipe reset` -#### Clear all modules in recipe: `recipe reset` +#### Delete a Recipe: `recipe delete` -#### Delete recipe: `recipe delete` +### Saving Recipe to File ## Command Summary @@ -185,4 +194,9 @@ Format: `exit` | List available modules | `list` | `list` | | Select processing module | `select ` | `select base64decode`
`select hexencode` | | Reset to original data | `reset` | `reset` | -| Exit program | `exit` | `exit` | \ No newline at end of file +| Exit program | `exit` | `exit` | + +## FAQ + +**Q:** How do I transfer my recipes to another computer? +**A:** Install the app in the other computer and copy the recipe files into the recipe folder. From b8fab531280f7fa8da673308fbd325e41dea55b9 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 27 Oct 2021 20:46:18 +0800 Subject: [PATCH 389/607] Add recipe commands --- docs/UserGuide.md | 95 ++++++++++++++++++++++++++++++++--- docs/images/recipeDelete.png | Bin 0 -> 12961 bytes docs/images/recipeList.png | Bin 0 -> 19990 bytes docs/images/recipeNew.png | Bin 0 -> 16997 bytes docs/images/recipePop.png | Bin 0 -> 14815 bytes docs/images/recipePush.png | Bin 0 -> 16657 bytes docs/images/recipeReset.png | Bin 0 -> 13677 bytes docs/images/recipeSelect.png | Bin 0 -> 17056 bytes 8 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 docs/images/recipeDelete.png create mode 100644 docs/images/recipeList.png create mode 100644 docs/images/recipeNew.png create mode 100644 docs/images/recipePop.png create mode 100644 docs/images/recipePush.png create mode 100644 docs/images/recipeReset.png create mode 100644 docs/images/recipeSelect.png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c87debb24b..de96eeb4e6 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -161,21 +161,100 @@ Exit the program. Format: `exit` -### Recipe Commands: `recipe` +## Recipe Commands: `recipe` -#### Create new recipe: `recipe new` +### Create a New Recipe: `recipe new` -#### Select recipe for editing: `recipe select` +Creates a new recipe and sets it as currently being edited. -#### List modules in recipe: `recipe list` +Format: `recipe new ` -#### Add module to recipe: `recipe push` +Example: -#### Remove module from recipe: `recipe pop` +- `recipe new testRecipe` Creates a new recipe with the name `testRecipe`. -#### Clear all modules in recipe: `recipe reset` +![recipeNew.png](images/recipeNew.png) -#### Delete recipe: `recipe delete` +### Select a Recipe for Editing: `recipe select` + +Sets a recipe as currently being edited. + +Format: `recipe select ` + +Example: + +- `recipe select testRecipe` Selects `testRecipe` as the recipe to be edited. + +![recipeSelect.png](images/recipeSelect.png) + +### List Modules in a Recipe: `recipe list` + +Show all the modules in a recipe, in the order they were added. + +Format: `recipe list ` + +Example: + +- `recipe list testRecipe` Lists the modules in the recipe named `testRecipe`. + +![recipeList.png](images/recipeList.png) + +### Add a Module into a Recipe: `recipe push` + +Adds a module into the recipe that is currently being edited. + +Format: `recipe push {moduleArgument}` + +Example: + +- `recipe push base64encode` Adds the `base64encode` module into the current editing recipe. +- `recipe push rotencode 13` Adds the `rotencode` module with a single argument `13` into the current editing recipe. + +![recipePush.png](images/recipePush.png) + +### Remove a Module from a Recipe: `recipe pop` + +Removes the most recently added module from the recipe that is currently being edited. + +Format: `recipe pop` + +![recipePop.png](images/recipePop.png) + +### Clear All Modules in a Recipe: `recipe reset` + +Removes all modules from the recipe that is currently being edited. + +Format: `recipe reset` + +![recipeReset.png](images/recipeReset.png) + +### Delete a Recipe: `recipe delete` + +Deletes a recipe from the program. + +Format: `recipe delete ` + +Example: + +- `recipe delete testRecipe` Deletes the recipe with the name `testRecipe`. +- +![recipeDelete.png](images/recipeDelete.png) + +## Saving Recipe to File + +Saves the recipe into a file. However, this is done automatically for you, so that it is less punishing for you (especially if you often forget to save your documents before exiting). + +How it works: + +Every time you add a recipe into Decodex, it will save this new recipe into a text file, with the recipe name as the file name, in the `recipe/` folder. Furthermore, any adding or removing of modules from this recipe would also update the corresponding recipe save file. + +Example: + +If you create a new recipe called "onlyForMe" in Decodex, this will create "onlyForMe.txt" which you can find in the `recipe/` folder. And whenever you run the `recipe push` or `recipe pop` command, it will update "onlyForMe.txt" accordingly. + +## Reading/writing data from/to file `[coming soon in the future]` + +Coming soon to a cinema near you in the future*…* ## Command Summary diff --git a/docs/images/recipeDelete.png b/docs/images/recipeDelete.png new file mode 100644 index 0000000000000000000000000000000000000000..f85f88f554b2d8f780f213f8e6495e889c2abd4d GIT binary patch literal 12961 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 HC-{E=2d)!4 literal 0 HcmV?d00001 diff --git a/docs/images/recipeList.png b/docs/images/recipeList.png new file mode 100644 index 0000000000000000000000000000000000000000..97ab59164209a6e3fb99cc4ff9bde7044e854c90 GIT binary patch literal 19990 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 literal 0 HcmV?d00001 diff --git a/docs/images/recipeNew.png b/docs/images/recipeNew.png new file mode 100644 index 0000000000000000000000000000000000000000..53b7aef31daf00c434711285168f74e6941c2b52 GIT binary patch literal 16997 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 BU`GG| literal 0 HcmV?d00001 diff --git a/docs/images/recipePop.png b/docs/images/recipePop.png new file mode 100644 index 0000000000000000000000000000000000000000..984aa1f7d2d1faa29cdf337cc372e4d0b8ddb376 GIT binary patch literal 14815 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 G?|%TBk}&xI literal 0 HcmV?d00001 diff --git a/docs/images/recipePush.png b/docs/images/recipePush.png new file mode 100644 index 0000000000000000000000000000000000000000..b023e3f548550e54aed2592dcd752da28c053c0b GIT binary patch literal 16657 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
U!O| zjk~t8QOHKP(~~hu5tCt!gyTK8#>%oIXOE=FpklIY@pmhCV&v0nB3L$=1BQI~v_)b< z9Ew^=Wdd)5Z2;~G2&y#UmueByE>Lj3Ijeh!pnbGaT1l&$6z@Z3(XVV^nU6C(5reC= zLuqP$c?KVVz}z=vL*`G0zWwT94vuf8oE-(sxdN8Ssw3qA#`K9G&B7rbgYqSTHs>QAlAb?q4Lm-C;Q zSXVSo;iSgd;;ccKdH?K4%TX=`B&Px4-cfhzqI)=){0{M5`Yikp-YHJ4=QL8!Chj&6K=JEg)5y_(}tTBgO-)PxFj3Kq#P(K5cyD z%siukzlvnpvHC^}f=uqk1HWCu%%t8XbXx%>2~MImS>+G8up%=52Am%8ajY_ZEW2%T z7T7wh)@M*rhxPDG(;}X_HqZo2w4Q)q(%xV7zRCif@*SY^Ir?ik2gC|ek+AL?hF>`) zZ`|yb3iSVxws}y=4Q{sov{iyb0E+sEUz+RGJKn#2D~Y9qPrwcA`dqo5dsE>G1?ugw zdYu!{Y%f?9I|-I)!{*e)Vp03s zF&Dn(_0Q*Si5O_R1*kFeSo-k^cmCx4ND7J)al76Q!y7jMuQ}<0FA|)~ zZ#p7x8V4uSEB?K<``zWP;O-6{G#o0H% z{#gXql;-E>fzZCN+jQjT>dJlN#Rg7hwBcV*_L<-g%e|=Wb_qv7H)mFdaux%rIHNl| zJ8|dfPx>rP36F~EURcP&AqhnVBPyZE)@xK;UmA}u(oog8UwrRr(EkEW C()nuu literal 0 HcmV?d00001 diff --git a/docs/images/recipeReset.png b/docs/images/recipeReset.png new file mode 100644 index 0000000000000000000000000000000000000000..20c9387e1a4218f8ef6c06b7ed14ddf044b5691b GIT binary patch literal 13677 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 Date: Wed, 27 Oct 2021 22:02:52 +0800 Subject: [PATCH 390/607] Added new basic command images. --- docs/images/ug/ListCommandExample.png | Bin 0 -> 59145 bytes docs/images/ug/SelectCommandExample.png | Bin 0 -> 29447 bytes docs/images/ug/inputCommandExample.png | Bin 0 -> 12331 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/ug/ListCommandExample.png create mode 100644 docs/images/ug/SelectCommandExample.png create mode 100644 docs/images/ug/inputCommandExample.png diff --git a/docs/images/ug/ListCommandExample.png b/docs/images/ug/ListCommandExample.png new file mode 100644 index 0000000000000000000000000000000000000000..a63d37d076f94f6035bb46d86025823a4322758d GIT binary patch literal 59145 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 Glm7=dee*H^ literal 0 HcmV?d00001 diff --git a/docs/images/ug/SelectCommandExample.png b/docs/images/ug/SelectCommandExample.png new file mode 100644 index 0000000000000000000000000000000000000000..27435622f5442a5b5c264f14f3cef4fe99ad2539 GIT binary patch literal 29447 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 JQ7q5j{69YA)xQ7$ literal 0 HcmV?d00001 diff --git a/docs/images/ug/inputCommandExample.png b/docs/images/ug/inputCommandExample.png new file mode 100644 index 0000000000000000000000000000000000000000..b56f047c12eaac41f482a0949ec86576b7bcd0a0 GIT binary patch literal 12331 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 NY3Tl4pl Date: Wed, 27 Oct 2021 22:03:19 +0800 Subject: [PATCH 391/607] Moved all the remaining UG's basic command portion over from notion. --- docs/UserGuide.md | 73 +++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c87debb24b..770f7c4d02 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -55,13 +55,14 @@ This section serves to help the user better understand the terminologies used in | 🎮 | Represents something optional that you can try out, mostly for exploring or fun. | ## Quick Start + 1. Ensure you have Java version 11 or above installed on your computer. 1. If you haven't, you may download it [here](https://www.oracle.com/java/technologies/downloads/#java11-linux) - > ❗ You should download the installation relative to your Operating System. + > :exclamation: You should download the installation relative to your Operating System. 2. Next, download the latest `decodex.jar` [here](https://github.com/AY2122S1-CS2113T-T10-3/tp/releases) 1. Simply click on the `decodex.jar` under "Assets" and the download should start. 3. After downloading, you can open up `command prompt`. - > 💡To open command prompt, press `win + r` at the same time, then type and enter `cmd`. + > :bulb: To open command prompt, press `win + r` at the same time, then type and enter `cmd`. 4. Then, you can now run the `decodex.jar` by typing in `java -jar decodex.jar` and the Decodex's prompt should appear. Also, please ensure that you are at the directory where `decodex.jar` is. 1. In the screenshot below, the `decodex.jar` is located in the `Downloads` folder. ![carbon(23).png](images/carbon(23).png) @@ -71,10 +72,12 @@ This section serves to help the user better understand the terminologies used in 3. `select module base64encode`: Selects and runs the base64-encoding module on the data. 4. `reset`: Resets the changes made to data - resetting to its original data. 5. `exit`: Exits the application. - > 🎮 Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` is using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! + > :video_game: Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` + > using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! 6. For more information on Decodex's features, please refer to the Features Section. ## List of Available Modules + 1. Base64 1. `base64encode` - Encodes the data using base64 format 2. `base64decode` - Decodes the data using base64 format @@ -115,37 +118,46 @@ Format: `input ` Examples: -- `input SGVsbG9Xb3JsZA==` Enter base64-encoded data. -- input  hello world Enter plain text as data. +- `input HelloWorld!` Enters plain text as data. +- `input SGVsbG9Xb3JsZA==` Enters base64-encoded data. -![carbon(1).png](images/carbon(1).png) +![carbon(1).png](images/ug/inputCommandExample.png) -> :exclamation: Note that the program will consider all characters as `data` following the first space character in the input command. Observe the leading and trailing whitespaces in the result of the second command in the above screenshot. +####List available modules or recipes: `list` -> :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. +Shows a list of all available modules or recipes. -####List available modules or recipes: `list` +Format: `list {category}` -Shows a list of all available modules. +> :information_source: The acceptable values of `category` are `modules` and `recipes` . -Format: `list` +> :information_source: When `category` is not specified, both lists of modules and recipes are printed. -![carbon(8).png](images/carbon(8).png) +Examples: + +- `list` Lists modules and recipes. +- `list modules` Lists available modules. +- `list recipes` Lists available recipes. + +![carbon(8).png](images/ug/ListCommandExample.png) #### Selection of module or recipe: `select` -Selects a module and processes the data accordingly. Subsequent selection of modules will process the transformed data output from the previous module. +Selects a module or recipe and processes the data accordingly. Subsequent selection of modules or recipes will process the transformed data output from the previous module or recipe. -Format: `select ` +Format: `select module {moduleArgument}`, `select recipe ` -`moduleName` is the name of an available module supported by the program. +> :information_source: `moduleName` is the name of an available module supported by Decodex. `moduleArgument` is an argument that certain modules accept as input. + +> :information_source: `recipeName` is the name of an available recipe that is loaded by the program. Examples: -- `select base64decode` Decode the base64 encoded data. -- `select hexencode` Encode the data into hexadecimal format. +- `select module base64decode` Runs the `base64decode` module on the current data. +- `select module rotencode 13` Runs the `rotencode` module with argument `13` on the current data. +- `select recipe testRecipe1` Runs the `testRecipe1` recipe on the current data. -![carbon(9).png](images/carbon(9).png) +![carbon(9).png](images/ug/SelectCommandExample.png) > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. @@ -161,6 +173,8 @@ Exit the program. Format: `exit` +> :video_game: If you are trying out the side-quest from earlier and can't seem to find the solution to it, you may refer to the solution in the picture at the start of the user guide! + ### Recipe Commands: `recipe` #### Create new recipe: `recipe new` @@ -179,10 +193,19 @@ Format: `exit` ## Command Summary -| Action | Command Format | Example Usage | -| ------------------------ | --------------------- | ------------------------------------------------------------ | -| Enter input data | `input ` | `input SGVsbG9Xb3JsZA==`
input  hello world  | -| List available modules | `list` | `list` | -| Select processing module | `select ` | `select base64decode`
`select hexencode` | -| Reset to original data | `reset` | `reset` | -| Exit program | `exit` | `exit` | \ No newline at end of file +| Action | Command Format | Example Usage | +|---------------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------| +| Enter input data | `input ` | `input HelloWorld!` | +| List available modules/recipe | `list {category}` | `list` `list module` `list recipe` | +| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode` `select module rotencode 13` `select recipe testRecipe1` | +| Reset to original data | `reset` | `reset` | +| Create new recipe | `recipe new ` | `recipe new testRecipe` | +| Select recipe for editing | `recipe select ` | `recipe select testRecipe` | +| List modules in recipe | `recipe list ` | `recipe list testRecipe` | +| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode` `recipe push rotencode 13` | +| Remove module from recipe | `recipe pop` | `recipe pop` | +| Clear all modules in recipe | `recipe reset` | `recipe reset` | +| Delete recipe | `recipe delete ` | `recipe delete testRecipe` | +| Exit program | `exit` | `exit` | + +## FAQ \ No newline at end of file From 61f35acc6aabb8f25be3ddab4fc6b6b40334fe0c Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 27 Oct 2021 22:08:32 +0800 Subject: [PATCH 392/607] Removed unnecessary callouts/quotes and the fixing of the heading for list --- docs/UserGuide.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 770f7c4d02..8d5219c3f0 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -123,7 +123,7 @@ Examples: ![carbon(1).png](images/ug/inputCommandExample.png) -####List available modules or recipes: `list` +#### List available modules or recipes: `list` Shows a list of all available modules or recipes. @@ -159,8 +159,6 @@ Examples: ![carbon(9).png](images/ug/SelectCommandExample.png) -> :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. - #### Resetting data: `reset` Resets the transformed data back to the original input. From 32ba3d0dd438f01862dc809b3d86b2ce73dae00c Mon Sep 17 00:00:00 2001 From: Aloysius Lee <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 23:35:20 +0800 Subject: [PATCH 393/607] Replace intro image --- docs/images/carbon(6).png | Bin 47135 -> 53359 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/carbon(6).png b/docs/images/carbon(6).png index 4c46db6571cac1b1a40c7694e781d04d36e2b5aa..e52224c7f086eb2f13f6312bf25242efec15f30d 100644 GIT binary patch literal 53359 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 Date: Fri, 29 Oct 2021 15:50:45 +0800 Subject: [PATCH 394/607] Update UserGuide.md --- docs/UserGuide.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index cde9972ee2..e94e3e33f7 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -130,6 +130,11 @@ Examples: ![carbon(1).png](images/ug/inputCommandExample.png) +> :exclamation: Note that the program will consider all characters as data following the first space character in the input command. Observe the leading and trailing whitespaces in the result of the second command in the above screenshot. + +> :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. + + #### List available modules or recipes: `list` Shows a list of all available modules or recipes. From 05bd0e69c440d091a0c7c6fdeff82a9d53ebc78e Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Fri, 29 Oct 2021 15:52:57 +0800 Subject: [PATCH 395/607] Added last minute line breaks to our UG Had to use commit to master, since will spend too much time if using own branch. --- docs/UserGuide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index e94e3e33f7..8e2f2ef491 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -285,13 +285,13 @@ Coming soon to a cinema near you in the future*…* | Action | Command Format | Example Usage | |---------------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------| | Enter input data | `input ` | `input HelloWorld!` | -| List available modules/recipe | `list {category}` | `list` `list module` `list recipe` | -| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode` `select module rotencode 13` `select recipe testRecipe1` | +| List available modules/recipe | `list {category}` | `list` `list module`
`list recipe` | +| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | | Reset to original data | `reset` | `reset` | | Create new recipe | `recipe new ` | `recipe new testRecipe` | | Select recipe for editing | `recipe select ` | `recipe select testRecipe` | | List modules in recipe | `recipe list ` | `recipe list testRecipe` | -| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode` `recipe push rotencode 13` | +| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | | Remove module from recipe | `recipe pop` | `recipe pop` | | Clear all modules in recipe | `recipe reset` | `recipe reset` | | Delete recipe | `recipe delete ` | `recipe delete testRecipe` | From 987ae1fbbbdab477ad4a3e052ae81813f7c0c123 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 29 Oct 2021 15:53:56 +0800 Subject: [PATCH 396/607] Update UserGuide.md --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 8e2f2ef491..3e0e4f0b69 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -130,7 +130,7 @@ Examples: ![carbon(1).png](images/ug/inputCommandExample.png) -> :exclamation: Note that the program will consider all characters as data following the first space character in the input command. Observe the leading and trailing whitespaces in the result of the second command in the above screenshot. +> :exclamation: Note that the program will consider all characters as data following the first space character in the input command. You may observe the leading and trailing whitespaces in the output. > :exclamation: Non-printable characters (e.g. null characters) may not be correctly displayed and could potentially scramble the user interface. From b7619db8180f818c56fa3762c0c4a8129eb17ad7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 29 Oct 2021 15:54:38 +0800 Subject: [PATCH 397/607] Update UserGuide.md --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3e0e4f0b69..10428b5a25 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -261,7 +261,7 @@ Format: `recipe delete ` Example: - `recipe delete testRecipe` Deletes the recipe with the name `testRecipe`. -- + ![recipeDelete.png](images/recipeDelete.png) ## Saving Recipe to File From c1cc7394caae66fa57f08a34415956b2c841dc4a Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 29 Oct 2021 16:04:07 +0800 Subject: [PATCH 398/607] Update UserGuide.md --- docs/UserGuide.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 10428b5a25..d5eb0f8aeb 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -187,7 +187,7 @@ Format: `exit` ### Recipe Commands: `recipe` -### Create a New Recipe: `recipe new` +#### Create a New Recipe: `recipe new` Creates a new recipe and sets it as currently being edited. @@ -199,7 +199,7 @@ Example: ![recipeNew.png](images/recipeNew.png) -### Select a Recipe for Editing: `recipe select` +#### Select a Recipe for Editing: `recipe select` Sets a recipe as currently being edited. @@ -211,7 +211,7 @@ Example: ![recipeSelect.png](images/recipeSelect.png) -### List Modules in a Recipe: `recipe list` +#### List Modules in a Recipe: `recipe list` Show all the modules in a recipe, in the order they were added. @@ -223,7 +223,7 @@ Example: ![recipeList.png](images/recipeList.png) -### Add a Module into a Recipe: `recipe push` +#### Add a Module into a Recipe: `recipe push` Adds a module into the recipe that is currently being edited. @@ -236,7 +236,7 @@ Example: ![recipePush.png](images/recipePush.png) -### Remove a Module from a Recipe: `recipe pop` +#### Remove a Module from a Recipe: `recipe pop` Removes the most recently added module from the recipe that is currently being edited. @@ -244,7 +244,7 @@ Format: `recipe pop` ![recipePop.png](images/recipePop.png) -### Clear All Modules in a Recipe: `recipe reset` +#### Clear All Modules in a Recipe: `recipe reset` Removes all modules from the recipe that is currently being edited. @@ -252,7 +252,7 @@ Format: `recipe reset` ![recipeReset.png](images/recipeReset.png) -### Delete a Recipe: `recipe delete` +#### Delete a Recipe: `recipe delete` Deletes a recipe from the program. From 4c0b7aa8f34f4f73f969c2ea8350461817b22e6b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 30 Oct 2021 16:35:03 +0800 Subject: [PATCH 399/607] Added the error icon to ui printing for the loading from storage --- src/main/java/decodex/ui/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index c8a3b401a2..aa0c9accb7 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -230,7 +230,7 @@ public void printNewRecipeCreated(String recipeName) { */ public void printFailedToLoadFromStorageMessage(ArrayList failedFilesMessageList) { for (String failedFileMessage : failedFilesMessageList) { - System.out.println(failedFileMessage); + System.out.println(ERROR_ICON + failedFileMessage); } } From 9b6d7c991749d1adad02962032ad164e828038a0 Mon Sep 17 00:00:00 2001 From: rizemon Date: Sat, 30 Oct 2021 17:23:49 +0800 Subject: [PATCH 400/607] Decouple Storage from RecipeManager * Remove Storage from RecipeManager constructor * Move Storage method calls in RecipeManager to Command level * Add Storage to Command run method * Add try-catch to Commands to deal with IO errors from Storage method calls The main reason why Storage calls are moved to the Command level is so that during Recipe*Commands, even if the Storage calls face IO errors, the normal message (like recipe added, recipe deleted etc) will still get printed. --- src/main/java/decodex/Decodex.java | 4 +- src/main/java/decodex/commands/Command.java | 4 +- .../java/decodex/commands/ExitCommand.java | 4 +- .../java/decodex/commands/HelpCommand.java | 4 +- .../java/decodex/commands/InputCommand.java | 4 +- .../java/decodex/commands/ListCommand.java | 4 +- .../java/decodex/commands/ResetCommand.java | 4 +- .../java/decodex/commands/SelectCommand.java | 4 +- .../commands/recipe/RecipeDeleteCommand.java | 9 ++++- .../commands/recipe/RecipeListCommand.java | 4 +- .../commands/recipe/RecipeNewCommand.java | 11 ++++- .../commands/recipe/RecipePopCommand.java | 9 ++++- .../commands/recipe/RecipePushCommand.java | 9 ++++- .../commands/recipe/RecipeResetCommand.java | 9 ++++- .../commands/recipe/RecipeSelectCommand.java | 4 +- .../java/decodex/recipes/RecipeManager.java | 21 +++------- .../decodex/commands/HelpCommandTest.java | 4 +- .../decodex/commands/InputCommandTest.java | 9 +++-- .../decodex/commands/ListCommandTest.java | 11 ++--- .../commands/RecipeDeleteCommandTest.java | 8 ++-- .../commands/RecipeListCommandTest.java | 12 +++--- .../commands/RecipeNewCommandTest.java | 8 ++-- .../commands/RecipePopCommandTest.java | 16 ++++---- .../commands/RecipePushCommandTest.java | 9 +++-- .../commands/RecipeResetCommandTest.java | 6 +-- .../commands/RecipeSelectCommandTest.java | 12 +++--- .../decodex/commands/ResetCommandTest.java | 8 ++-- .../decodex/commands/SelectCommandTest.java | 40 ++++++++++--------- .../decodex/recipes/RecipeManagerTest.java | 3 +- 29 files changed, 150 insertions(+), 104 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index aaeddbf18c..beeff9b339 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -48,7 +48,7 @@ private void initDecodex() { parser = new Parser(); ui = new Ui(); storage = new Storage(); - recipeManager = new RecipeManager(storage); + recipeManager = new RecipeManager(); try { loadSavedRecipes(); } catch (IOException err) { @@ -82,7 +82,7 @@ public void run() { try { command = parser.parseCommand(userInput); assert command != null : "Command should not be null"; - command.run(dataManager, moduleManager, ui, recipeManager); + command.run(dataManager, moduleManager, ui, recipeManager, storage); } catch (ParserException | CommandException | ModuleManagerException | DataManagerException | ModuleException | RecipeException | RecipeManagerException | IOException err) { ui.printError(err); diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index 2a1634c809..990d08d0f6 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -9,6 +9,7 @@ import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import java.io.IOException; @@ -20,7 +21,8 @@ public abstract class Command { public Command() { } - public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, ModuleManagerException, DataManagerException, ModuleException, RecipeException, RecipeManagerException, IOException; } diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/commands/ExitCommand.java index 581de092c4..b01d91217f 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/commands/ExitCommand.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; public class ExitCommand extends Command { @@ -16,7 +17,8 @@ public ExitCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) { ui.printGoodbye(); } } diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java index 5ae0a2cbd3..aa3f478d43 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -10,6 +10,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; // @@author rizemon @@ -42,7 +43,8 @@ public HelpCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) { ui.printCommandHelp(BASIC_COMMAND_HELP_MESSAGES, RECIPE_COMMAND_HELP_MESSAGES); } } diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index 6e2366db4b..27f515ded6 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -5,6 +5,7 @@ import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -23,7 +24,8 @@ public InputCommand(String dataString) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException { if (dataString.isEmpty()) { throw new CommandException(ErrorMessages.MISSING_ARGUMENT); diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 4664bef7c2..69b62d636e 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -4,6 +4,7 @@ import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -27,7 +28,8 @@ public ListCommand(String listCategory) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException { boolean isPrintModuleList = listCategory == null || listCategory.equals(LIST_CATEGORY_MODULES); boolean isPrintRecipeList = listCategory == null || listCategory.equals(LIST_CATEGORY_RECIPE); diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java index 4e7127fdb2..bebd33f220 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -3,6 +3,7 @@ import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.RegularMessages; @@ -18,7 +19,8 @@ public ResetCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) { dataManager.resetToOriginalData(); ui.printSuccess(RegularMessages.REVERTED_ALL_CHANGES); } diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index b8abc22288..4b256e6e3f 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -12,6 +12,7 @@ import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -36,7 +37,8 @@ public SelectCommand(String selectCategory, String itemName, String[] parameters } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws ModuleManagerException, CommandException, DataManagerException, ModuleException, RecipeException, RecipeManagerException { assert selectCategory != null : "selectCategory should not be null"; diff --git a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java index c25412167b..1fb550f3ac 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java @@ -7,6 +7,7 @@ import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; import java.io.IOException; @@ -25,13 +26,19 @@ public RecipeDeleteCommand(String recipeName) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, ModuleException, RecipeManagerException, IOException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } recipeManager.removeRecipe(recipeName); + try { + storage.deleteRecipeFile(recipeName); + } catch (IOException err) { + ui.printError(err); + } ui.printRecipeDeleted(recipeName); } } diff --git a/src/main/java/decodex/commands/recipe/RecipeListCommand.java b/src/main/java/decodex/commands/recipe/RecipeListCommand.java index 8fb9d6ed4a..b8b519423f 100644 --- a/src/main/java/decodex/commands/recipe/RecipeListCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeListCommand.java @@ -9,6 +9,7 @@ import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.RegularMessages; import java.util.ArrayList; @@ -28,7 +29,8 @@ public RecipeListCommand(String recipeName) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, ModuleException, RecipeManagerException { Recipe recipe; diff --git a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java index a5ce1e2812..ffe353bf40 100644 --- a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java @@ -9,6 +9,7 @@ import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; import java.io.IOException; @@ -27,8 +28,9 @@ public RecipeNewCommand(String recipeName) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, ModuleException, RecipeException, RecipeManagerException, IOException { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) + throws CommandException, ModuleException, RecipeException, RecipeManagerException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } @@ -36,6 +38,11 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec Recipe recipe = new Recipe(recipeName); recipeManager.addRecipe(recipe); + try { + storage.saveRecipeToFile(recipe); + } catch (IOException err) { + ui.printError(err); + } recipeManager.selectRecipeForEditing(recipeName); ui.printNewRecipeCreated(recipe.getName()); diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java index 950892a243..1c4a186fbf 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -8,6 +8,7 @@ import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import java.io.IOException; @@ -23,10 +24,16 @@ public RecipePopCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws RecipeException, RecipeManagerException, IOException { Module module = recipeManager.popModuleFromEditedRecipe(); Recipe editingRecipe = recipeManager.getEditingRecipe(); + try { + storage.saveRecipeToFile(editingRecipe); + } catch (IOException err) { + ui.printError(err); + } ui.printModuleRemovedFromRecipe(module.getName(), editingRecipe.getName()); } } diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java index 7e6f43589b..f299b39e00 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -10,6 +10,7 @@ import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; import java.io.IOException; @@ -31,7 +32,8 @@ public RecipePushCommand(String moduleName, String[] parameters) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException, IOException { if (moduleName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); @@ -40,6 +42,11 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec Module module = moduleManager.selectModule(moduleName, parameters); Recipe editingRecipe = recipeManager.getEditingRecipe(); recipeManager.pushModuleIntoEditedRecipe(module); + try { + storage.saveRecipeToFile(editingRecipe); + } catch (IOException err) { + ui.printError(err); + } ui.printModuleAddedToRecipe(module.getName(), editingRecipe.getName()); } } diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java index 2bf644df78..338f886dc8 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -6,6 +6,7 @@ import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import java.io.IOException; @@ -21,10 +22,16 @@ public RecipeResetCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws RecipeManagerException, IOException { Recipe editingRecipe = recipeManager.getEditingRecipe(); recipeManager.resetEditedRecipe(); + try { + storage.saveRecipeToFile(editingRecipe); + } catch (IOException err) { + ui.printError(err); + } ui.printRecipeReset(editingRecipe.getName()); } } diff --git a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java index 3b050b583e..c6f7a18077 100644 --- a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java @@ -6,6 +6,7 @@ import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -24,7 +25,8 @@ public RecipeSelectCommand(String recipeName) { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, RecipeManagerException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index e75d4728fb..e6adf78e37 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -1,6 +1,5 @@ package decodex.recipes; -import decodex.storage.Storage; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; @@ -32,15 +31,12 @@ public class RecipeManager { */ private String editingRecipeName; - private Storage storage; - /** * Creates a new RecipeManager with no recipes. */ - public RecipeManager(Storage storage) { + public RecipeManager() { recipeList = new HashMap<>(); editingRecipeName = null; - this.storage = storage; } /** @@ -61,13 +57,12 @@ public String[] getRecipeNames() { * @param recipe The recipe to be added to the recipe manager. * @throws RecipeManagerException If the given recipe name already exists in recipe manager. */ - public void addRecipe(Recipe recipe) throws RecipeManagerException, IOException { + public void addRecipe(Recipe recipe) throws RecipeManagerException { if (recipeList.containsKey(recipe.getName())) { throw new RecipeManagerException(ErrorMessages.DUPLICATE_RECIPE_NAME_MESSAGE); } recipeList.put(recipe.getName(), recipe); logger.fine(String.format("[RecipeManager] Added recipe %s", recipe.getName())); - storage.saveRecipeToFile(recipe); } /** @@ -77,14 +72,13 @@ public void addRecipe(Recipe recipe) throws RecipeManagerException, IOException * @return The recipe that was removed. * @throws RecipeManagerException If the recipe could not be found in the recipe manager. */ - public Recipe removeRecipe(String name) throws RecipeManagerException, IOException { + public Recipe removeRecipe(String name) throws RecipeManagerException { Recipe recipeToRemove = getRecipe(name); recipeList.remove(name); if (editingRecipeName != null && editingRecipeName.equals(name)) { editingRecipeName = null; } logger.fine(String.format("[RecipeManager] Removed recipe %s", recipeToRemove.getName())); - storage.deleteRecipeFile(name); return recipeToRemove; } @@ -136,10 +130,9 @@ public void selectRecipeForEditing(String name) throws RecipeManagerException { * @param module The module to be added to the recipe. * @throws RecipeManagerException If no recipe is selected as currently being edited. */ - public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerException, IOException { + public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerException { Recipe editingRecipe = getEditingRecipe(); editingRecipe.push(module); - storage.saveRecipeToFile(editingRecipe); } /** @@ -148,10 +141,9 @@ public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerExcept * @return The module that was popped from the recipe. * @throws RecipeManagerException If no recipe is selected as currently being edited. */ - public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeException, IOException { + public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeException { Recipe editingRecipe = getEditingRecipe(); Module module = editingRecipe.pop(); - storage.saveRecipeToFile(editingRecipe); return module; } @@ -160,10 +152,9 @@ public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeE * * @throws RecipeManagerException If no recipe is selected as currently being edited. */ - public void resetEditedRecipe() throws RecipeManagerException, IOException { + public void resetEditedRecipe() throws RecipeManagerException { Recipe editingRecipe = getEditingRecipe(); editingRecipe.reset(); - storage.saveRecipeToFile(editingRecipe); } // @@author } diff --git a/src/test/java/decodex/commands/HelpCommandTest.java b/src/test/java/decodex/commands/HelpCommandTest.java index 676446bab3..73e1611836 100644 --- a/src/test/java/decodex/commands/HelpCommandTest.java +++ b/src/test/java/decodex/commands/HelpCommandTest.java @@ -24,7 +24,7 @@ class HelpCommandTest { private final DataManager dataManager = new DataManager(); private final ModuleManager moduleManager = new ModuleManager(); private final Storage storage = new Storage(); - private final RecipeManager recipeManager = new RecipeManager(storage); + private final RecipeManager recipeManager = new RecipeManager(); private final Ui ui = new Ui(); @BeforeEach @@ -41,7 +41,7 @@ public void restoreOutputStream() { @Test public void run_allCommands_success() { HelpCommand command = new HelpCommand(); - command.run(dataManager, moduleManager, ui, recipeManager); + command.run(dataManager, moduleManager, ui, recipeManager, storage); assertFalse(outputStream.toString().isBlank()); } } diff --git a/src/test/java/decodex/commands/InputCommandTest.java b/src/test/java/decodex/commands/InputCommandTest.java index b6f0e4b455..fc5c842185 100644 --- a/src/test/java/decodex/commands/InputCommandTest.java +++ b/src/test/java/decodex/commands/InputCommandTest.java @@ -24,11 +24,12 @@ public void run_missingArguments_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String dataString = ""; InputCommand testCommand = new InputCommand(dataString); - assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); } @Test @@ -37,11 +38,11 @@ public void run_insertData_insertedData() DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String dataString = "something"; InputCommand testCommand = new InputCommand(dataString); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); Data testData = new Data(dataString); assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); } diff --git a/src/test/java/decodex/commands/ListCommandTest.java b/src/test/java/decodex/commands/ListCommandTest.java index 9681991c3f..7b3b15f7fb 100644 --- a/src/test/java/decodex/commands/ListCommandTest.java +++ b/src/test/java/decodex/commands/ListCommandTest.java @@ -25,7 +25,7 @@ public class ListCommandTest { private final DataManager dataManager = new DataManager(); private final ModuleManager moduleManager = new ModuleManager(); private final Storage storage = new Storage(); - private final RecipeManager recipeManager = new RecipeManager(storage); + private final RecipeManager recipeManager = new RecipeManager(); private final Ui ui = new Ui(); @BeforeEach @@ -43,7 +43,7 @@ public void restoreOutputStream() { public void run_listNull_success() throws CommandException { String listCategory = null; ListCommand listCommand = new ListCommand(listCategory); - listCommand.run(dataManager, moduleManager, ui, recipeManager); + listCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertFalse(outputStream.toString().isBlank()); } @@ -51,7 +51,7 @@ public void run_listNull_success() throws CommandException { public void run_listModules_success() throws CommandException { String listCategory = "modules"; ListCommand listCommand = new ListCommand(listCategory); - listCommand.run(dataManager, moduleManager, ui, recipeManager); + listCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertFalse(outputStream.toString().isBlank()); } @@ -59,7 +59,7 @@ public void run_listModules_success() throws CommandException { public void run_listRecipes_success() throws CommandException { String listCategory = "recipes"; ListCommand listCommand = new ListCommand(listCategory); - listCommand.run(dataManager, moduleManager, ui, recipeManager); + listCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertFalse(outputStream.toString().isBlank()); } @@ -67,6 +67,7 @@ public void run_listRecipes_success() throws CommandException { public void run_listUnknown_throwsCommandException() { String listCategory = "unknown"; ListCommand listCommand = new ListCommand(listCategory); - assertThrows(CommandException.class, () -> listCommand.run(dataManager, moduleManager, ui, recipeManager)); + assertThrows(CommandException.class, () -> listCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); } } diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java index 4a0ede3ef9..b034b79b6f 100644 --- a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java @@ -29,12 +29,12 @@ void run_deleteValidRecipe_recipeDeleted() throws CommandException, RecipeExcept ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); recipeManager.addRecipe(new Recipe(testRecipeName)); Command recipeDeleteCommand = new RecipeDeleteCommand(testRecipeName); - recipeDeleteCommand.run(dataManager, moduleManager, ui, recipeManager); + recipeDeleteCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertThrows(RecipeManagerException.class, () -> recipeManager.getRecipe(testRecipeName)); } @@ -45,7 +45,7 @@ void run_deleteInvalidRecipe_expectException() throws RecipeException, RecipeMan ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); recipeManager.addRecipe(new Recipe("BaconPancakes")); @@ -53,6 +53,6 @@ void run_deleteInvalidRecipe_expectException() throws RecipeException, RecipeMan Command recipeDeleteCommand = new RecipeDeleteCommand(testRecipeName); assertThrows(RecipeManagerException.class, - () -> recipeDeleteCommand.run(dataManager, moduleManager, ui, recipeManager)); + () -> recipeDeleteCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } } diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index 212739aa2c..c662ef4ee8 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -35,7 +35,7 @@ class RecipeListCommandTest { private final DataManager dataManager = new DataManager(); private final ModuleManager moduleManager = new ModuleManager(); private final Storage storage = new Storage(); - private final RecipeManager recipeManager = new RecipeManager(storage); + private final RecipeManager recipeManager = new RecipeManager(); private final Ui ui = new Ui(); @BeforeEach @@ -64,7 +64,7 @@ public void restoreOutputStream() { @Test void run_listRecipeName_success() throws RecipeManagerException, CommandException, ModuleException { RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertFalse(outputStream.toString().isBlank()); } @@ -72,14 +72,14 @@ void run_listRecipeName_success() throws RecipeManagerException, CommandExceptio void run_listBlankNoEditingRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand(""); assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } @Test void run_listBlankHasEditingRecipe_success() throws RecipeManagerException, CommandException, ModuleException { recipeManager.selectRecipeForEditing(TEST_RECIPE_NAME); RecipeListCommand testCommand = new RecipeListCommand(""); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertFalse(outputStream.toString().isBlank()); } @@ -87,14 +87,14 @@ void run_listBlankHasEditingRecipe_success() throws RecipeManagerException, Comm void run_listUnknownRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand("unknownRecipe"); assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } @Test void run_listEmptyRecipe_expectException() throws RecipeManagerException, CommandException, ModuleException { recipeManager.getRecipe(TEST_RECIPE_NAME).reset(); RecipeListCommand testCommand = new RecipeListCommand(TEST_RECIPE_NAME); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertFalse(outputStream.toString().isBlank()); } } diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/commands/RecipeNewCommandTest.java index ade121b14e..f066f867f3 100644 --- a/src/test/java/decodex/commands/RecipeNewCommandTest.java +++ b/src/test/java/decodex/commands/RecipeNewCommandTest.java @@ -30,9 +30,9 @@ void run_createNewRecipe_newRecipeCreated() throws CommandException, RecipeExcep ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); - newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager); + newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertNotNull(recipeManager.getRecipe("BaconPancakes")); } @@ -46,9 +46,9 @@ void run_createNewRecipeWithNoName_expectException() { ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); assertThrows(CommandException.class, - () -> newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager)); + () -> newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } } diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/commands/RecipePopCommandTest.java index fdc6de1ac1..cdc5d735cc 100644 --- a/src/test/java/decodex/commands/RecipePopCommandTest.java +++ b/src/test/java/decodex/commands/RecipePopCommandTest.java @@ -24,8 +24,7 @@ class RecipePopCommandTest { @Test public void run_oneModuleToEditingRecipe_recipeSizeIsZero() throws RecipeManagerException, ModuleManagerException, ModuleException, RecipeException, IOException { - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "test"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -40,7 +39,8 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsZero() RecipePopCommand testCommand = new RecipePopCommand(); DataManager dataManager = new DataManager(); Ui ui = new Ui(); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + Storage storage = new Storage(); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); } @@ -49,17 +49,15 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsZero() public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException, IOException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); - + Storage storage = new Storage(); String testRecipeName = "test"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); recipeManager.selectRecipeForEditing(testRecipeName); - RecipePopCommand testCommand = new RecipePopCommand(); - - assertThrows(RecipeException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + assertThrows(RecipeException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); } } diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java index 6424de7152..11a32a0cd8 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -28,7 +28,7 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsOne() DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String testRecipeName = "test"; @@ -40,7 +40,7 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsOne() String[] parameters = {}; RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 1); } @@ -50,7 +50,7 @@ public void run_emptyModuleName_expectException() throws RecipeException, Recipe DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String testRecipeName = "test"; @@ -63,6 +63,7 @@ public void run_emptyModuleName_expectException() throws RecipeException, Recipe RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); - assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); } } diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/commands/RecipeResetCommandTest.java index 4557c07c60..b9850e6191 100644 --- a/src/test/java/decodex/commands/RecipeResetCommandTest.java +++ b/src/test/java/decodex/commands/RecipeResetCommandTest.java @@ -24,8 +24,7 @@ class RecipeResetCommandTest { @Test public void run_oneModuleInEditingRecipe_recipeSizeIsZero() throws RecipeException, RecipeManagerException, ModuleManagerException, ModuleException, IOException { - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "test"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -40,7 +39,8 @@ public void run_oneModuleInEditingRecipe_recipeSizeIsZero() throws RecipeExcepti RecipeResetCommand testCommand = new RecipeResetCommand(); DataManager dataManager = new DataManager(); Ui ui = new Ui(); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + Storage storage = new Storage(); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); } diff --git a/src/test/java/decodex/commands/RecipeSelectCommandTest.java b/src/test/java/decodex/commands/RecipeSelectCommandTest.java index 1adf28b476..30401ca9f0 100644 --- a/src/test/java/decodex/commands/RecipeSelectCommandTest.java +++ b/src/test/java/decodex/commands/RecipeSelectCommandTest.java @@ -26,7 +26,7 @@ public void run_selectExistingRecipe_existingRecipe() throws RecipeException, Re DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String testRecipeName = "test"; @@ -34,7 +34,7 @@ public void run_selectExistingRecipe_existingRecipe() throws RecipeException, Re recipeManager.addRecipe(testRecipe); RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals(recipeManager.getEditingRecipe().getName(), testRecipeName); } @@ -43,14 +43,14 @@ public void run_selectNonExistentRecipe_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String testRecipeName = "test"; RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } @Test @@ -58,13 +58,13 @@ public void run_nullRecipe_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String testRecipeName = ""; RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); assertThrows(CommandException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } } diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index cdd771cf46..32495de42f 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -23,14 +23,14 @@ public void run_noChanges_sameOriginalData() throws DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data originalData = new Data("hi"); dataManager.setOriginalData(originalData); ResetCommand testCommand = new ResetCommand(); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); } @@ -39,7 +39,7 @@ public void run_newData_sameOriginalData() throws DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data originalData = new Data("hi"); @@ -48,7 +48,7 @@ public void run_newData_sameOriginalData() throws DataManagerException { dataManager.setCurrentData(newData); ResetCommand testCommand = new ResetCommand(); - testCommand.run(dataManager, moduleManager, ui, recipeManager); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); } } diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index 251f709eed..bb52846c2c 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -29,7 +29,7 @@ public void run_validModuleName_success() throws ModuleManagerException, Command DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -39,7 +39,7 @@ public void run_validModuleName_success() throws ModuleManagerException, Command String moduleName = "base64encode"; String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); - selectCommand.run(dataManager, moduleManager, ui, recipeManager); + selectCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals("aGVsbG8gd29ybGQ=", dataManager.getCurrentData().toString()); } @@ -51,7 +51,7 @@ public void run_moduleWithParameters_success() throws ModuleManagerException, Co ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Data data = new Data("hello world"); dataManager.setOriginalData(data); @@ -60,7 +60,7 @@ public void run_moduleWithParameters_success() throws ModuleManagerException, Co String moduleName = "rotencode"; String[] parameters = {"13"}; SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); - selectCommand.run(dataManager, moduleManager, ui, recipeManager); + selectCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals("uryyb jbeyq", dataManager.getCurrentData().toString()); } @@ -70,7 +70,7 @@ public void run_blankModuleName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -81,7 +81,8 @@ public void run_blankModuleName_expectException() { String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); - assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); + assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); } @Test @@ -89,7 +90,7 @@ public void run_unknownModuleName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -101,7 +102,7 @@ public void run_unknownModuleName_expectException() { SelectCommand selectCommand = new SelectCommand(selectCategory, moduleName, parameters); assertThrows(ModuleManagerException.class, () -> selectCommand.run(dataManager, moduleManager, ui, - recipeManager)); + recipeManager, storage)); } @Test @@ -111,8 +112,7 @@ public void run_validRecipeName_success() throws ModuleManagerException, Command Data data = new Data("hello world"); dataManager.setOriginalData(data); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); String recipeName = "testRecipe"; Recipe recipe = new Recipe(recipeName); recipe.push(new HexEncoder()); @@ -121,11 +121,12 @@ public void run_validRecipeName_success() throws ModuleManagerException, Command ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); + Storage storage = new Storage(); String selectCategory = "recipe"; String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); - selectCommand.run(dataManager, moduleManager, ui, recipeManager); + selectCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals("Njg2NTZjNmM2ZjIwNzc2ZjcyNmM2NA==", dataManager.getCurrentData().toString()); } @@ -139,7 +140,7 @@ public void run_emptyRecipe_success() throws ModuleManagerException, CommandExce dataManager.setOriginalData(data); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); String recipeName = "testRecipe"; Recipe recipe = new Recipe(recipeName); recipeManager.addRecipe(recipe); @@ -150,7 +151,7 @@ public void run_emptyRecipe_success() throws ModuleManagerException, CommandExce String selectCategory = "recipe"; String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); - selectCommand.run(dataManager, moduleManager, ui, recipeManager); + selectCommand.run(dataManager, moduleManager, ui, recipeManager, storage); assertEquals(dataString, dataManager.getCurrentData().toString()); } @@ -160,7 +161,7 @@ public void run_blankRecipeName_expectRecipeManagerException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String selectCategory = "recipe"; @@ -169,7 +170,7 @@ public void run_blankRecipeName_expectRecipeManagerException() { SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); assertThrows(RecipeManagerException.class, () -> selectCommand.run(dataManager, moduleManager, ui, - recipeManager)); + recipeManager, storage)); } @Test @@ -177,7 +178,7 @@ public void run_unknownRecipe_expectRecipeManagerException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String selectCategory = "recipe"; @@ -186,7 +187,7 @@ public void run_unknownRecipe_expectRecipeManagerException() { SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); assertThrows(RecipeManagerException.class, () -> selectCommand.run(dataManager, moduleManager, ui, - recipeManager)); + recipeManager, storage)); } @Test @@ -194,7 +195,7 @@ public void run_invalidSelectCategory_expectCommandException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(storage); + RecipeManager recipeManager = new RecipeManager(); Ui ui = new Ui(); String selectCategory = "unknownCategory"; @@ -202,6 +203,7 @@ public void run_invalidSelectCategory_expectCommandException() { String[] parameters = {}; SelectCommand selectCommand = new SelectCommand(selectCategory, recipeName, parameters); - assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager)); + assertThrows(CommandException.class, () -> selectCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); } } diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index f36f302efd..0622ca2c07 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -15,12 +15,11 @@ // @@author rizemon class RecipeManagerTest { - private final Storage storage = new Storage(); private RecipeManager recipeManager; @BeforeEach public void createNewRecipeManager() { - recipeManager = new RecipeManager(storage); + recipeManager = new RecipeManager(); } @Test From 87ac3523de631f2635294e64ff5464bbb7a6225d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 30 Oct 2021 17:30:48 +0800 Subject: [PATCH 401/607] Reverted to using a simple fix for loading recipes from storage at start. If the file path exists eg. xxxx/recipe then do another check if it isn't a directory, throw an error prompting user to change it to allow the saving of recipes to work properly. This is to fix bug#1 found in our discord (you may request access for this through our issues or email us) --- src/main/java/decodex/Decodex.java | 5 +++-- .../decodex/data/exception/StorageException.java | 12 ++++++++++++ src/main/java/decodex/storage/Storage.java | 11 +++++++++-- src/main/java/decodex/ui/messages/ErrorMessages.java | 3 +++ 4 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 src/main/java/decodex/data/exception/StorageException.java diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index aaeddbf18c..18b80e3a8d 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -10,6 +10,7 @@ import decodex.data.exception.ParserException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; import decodex.modules.ModuleManager; import decodex.parser.Parser; import decodex.recipes.RecipeManager; @@ -51,7 +52,7 @@ private void initDecodex() { recipeManager = new RecipeManager(storage); try { loadSavedRecipes(); - } catch (IOException err) { + } catch (IOException | StorageException err) { ui.printError(err); } @@ -91,7 +92,7 @@ public void run() { } while (!(command instanceof ExitCommand)); } - private void loadSavedRecipes() throws IOException { + private void loadSavedRecipes() throws IOException, StorageException { storage.loadRecipesFromDirectory(moduleManager, recipeManager, ui); } } diff --git a/src/main/java/decodex/data/exception/StorageException.java b/src/main/java/decodex/data/exception/StorageException.java new file mode 100644 index 0000000000..e5a054b756 --- /dev/null +++ b/src/main/java/decodex/data/exception/StorageException.java @@ -0,0 +1,12 @@ +package decodex.data.exception; + +/** + * This exception is raised when Storage errors occur. + */ +public class StorageException extends Exception { + + public StorageException(String message) { + super(message); + } +} + diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index b81a59c10d..49340e16ae 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -4,6 +4,7 @@ import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; import decodex.modules.ModuleManager; import decodex.modules.Module; import decodex.recipes.Recipe; @@ -75,7 +76,7 @@ public Storage() { * @throws ModuleManagerException If provided module name is not an available module. */ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager recipeManager, Ui ui) - throws IOException { + throws IOException, StorageException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); File[] recipeFiles = getAllRecipeFiles(); @@ -107,8 +108,14 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager * * @return The list of recipe File objects. */ - private File[] getAllRecipeFiles() { + private File[] getAllRecipeFiles() throws StorageException { File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); + + if (!recipeDirectory.isDirectory()) { + // throw error if not a valid directory. + throw new StorageException(ErrorMessages.INVALID_RECIPE_DIRECTORY_FILETYPE); + } + File[] files = recipeDirectory.listFiles(); File[] recipeFiles = Arrays.stream(files) .filter(file -> file.isFile()) diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index afe6dee560..a67bf77c2a 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -34,4 +34,7 @@ public class ErrorMessages { public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = " failed to be loaded into Decodex"; public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; + public static final String INVALID_RECIPE_DIRECTORY_FILETYPE = "The recipe folder does not seem to be a valid folder, " + + "but rather a file, please change it " + + "to ensure that you can save your recipes created properly"; } From 26e251bfba25cd8f2355f9e45649ce192e773974 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 30 Oct 2021 17:45:08 +0800 Subject: [PATCH 402/607] Fixed the gradle check of checkstylemain --- src/main/java/decodex/ui/messages/ErrorMessages.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index a67bf77c2a..d25eff9707 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -34,7 +34,7 @@ public class ErrorMessages { public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = " failed to be loaded into Decodex"; public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; - public static final String INVALID_RECIPE_DIRECTORY_FILETYPE = "The recipe folder does not seem to be a valid folder, " - + "but rather a file, please change it " + public static final String INVALID_RECIPE_DIRECTORY_FILETYPE = "The recipe folder does not seem " + + "to be a valid folder, but rather a file, please change it " + "to ensure that you can save your recipes created properly"; } From 8cdee750d59610b74d9ff01f7e66eeae89bb200a Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 31 Oct 2021 12:52:50 +0800 Subject: [PATCH 403/607] Added author tag and the additional space. --- src/main/java/decodex/ui/Ui.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index aa0c9accb7..0fefe6638d 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -223,6 +223,7 @@ public void printNewRecipeCreated(String recipeName) { printSuccess(String.format(RegularMessages.NEW_RECIPE_CREATED, recipeName)); } + // @@author Kair0s3 /** * Prints the list of message for the recipe files that failed to load. * @@ -230,9 +231,10 @@ public void printNewRecipeCreated(String recipeName) { */ public void printFailedToLoadFromStorageMessage(ArrayList failedFilesMessageList) { for (String failedFileMessage : failedFilesMessageList) { - System.out.println(ERROR_ICON + failedFileMessage); + System.out.println(ERROR_ICON + " " + failedFileMessage); } } + // @@author /** * Prints the prompt header. From cd8e7867b878981ee880f8fc91864b0e4f42d37d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 31 Oct 2021 12:53:23 +0800 Subject: [PATCH 404/607] Moved the checking of valid directory and exist to instantiate if not exist method. --- .../commands/recipe/RecipeNewCommand.java | 3 ++- .../commands/recipe/RecipePopCommand.java | 3 ++- .../commands/recipe/RecipePushCommand.java | 3 ++- .../commands/recipe/RecipeResetCommand.java | 3 ++- src/main/java/decodex/storage/Storage.java | 18 +++++++++--------- .../decodex/ui/messages/ErrorMessages.java | 6 ++---- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java index ffe353bf40..d2997b2f1d 100644 --- a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java @@ -6,6 +6,7 @@ import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; @@ -40,7 +41,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec recipeManager.addRecipe(recipe); try { storage.saveRecipeToFile(recipe); - } catch (IOException err) { + } catch (IOException | StorageException err) { ui.printError(err); } recipeManager.selectRecipeForEditing(recipeName); diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java index 1c4a186fbf..1e93db96c1 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -4,6 +4,7 @@ import decodex.data.DataManager; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; @@ -31,7 +32,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec Recipe editingRecipe = recipeManager.getEditingRecipe(); try { storage.saveRecipeToFile(editingRecipe); - } catch (IOException err) { + } catch (IOException | StorageException err) { ui.printError(err); } ui.printModuleRemovedFromRecipe(module.getName(), editingRecipe.getName()); diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java index f299b39e00..93ca864b87 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -6,6 +6,7 @@ import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.StorageException; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; @@ -44,7 +45,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec recipeManager.pushModuleIntoEditedRecipe(module); try { storage.saveRecipeToFile(editingRecipe); - } catch (IOException err) { + } catch (IOException | StorageException err) { ui.printError(err); } ui.printModuleAddedToRecipe(module.getName(), editingRecipe.getName()); diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java index 338f886dc8..c6081926fd 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -3,6 +3,7 @@ import decodex.commands.Command; import decodex.data.DataManager; import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; @@ -29,7 +30,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec recipeManager.resetEditedRecipe(); try { storage.saveRecipeToFile(editingRecipe); - } catch (IOException err) { + } catch (IOException | StorageException err) { ui.printError(err); } ui.printRecipeReset(editingRecipe.getName()); diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 49340e16ae..3c4092278b 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -111,11 +111,6 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager private File[] getAllRecipeFiles() throws StorageException { File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); - if (!recipeDirectory.isDirectory()) { - // throw error if not a valid directory. - throw new StorageException(ErrorMessages.INVALID_RECIPE_DIRECTORY_FILETYPE); - } - File[] files = recipeDirectory.listFiles(); File[] recipeFiles = Arrays.stream(files) .filter(file -> file.isFile()) @@ -182,7 +177,7 @@ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleM * @throws IOException If an error occurred when reading the file or * the input file does not exist. */ - public byte[] readInputFromFile(String fileName) throws IOException { + public byte[] readInputFromFile(String fileName) throws IOException, StorageException { instantiateDirectoryIfNotExist(DEFAULT_INPUT_DIRECTORY); File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); @@ -218,7 +213,7 @@ private byte[] readContentFromFile(Path readFilePath) throws IOException, FileNo * @throws IOException If an error occurred when creating the file * or when writing to the file. */ - public void saveOutputToFile(byte[] outputBytes) throws IOException { + public void saveOutputToFile(byte[] outputBytes) throws IOException, StorageException { instantiateDirectoryIfNotExist(DEFAULT_OUTPUT_DIRECTORY); LocalDateTime currentDateTime = LocalDateTime.now(); @@ -255,7 +250,7 @@ private void saveBytesToFile(File writeFile, byte[] contentBytes) throws IOExcep * @throws IOException If an error occurred when creating the file * or when writing to the file. */ - public void saveRecipeToFile(Recipe recipe) throws IOException { + public void saveRecipeToFile(Recipe recipe) throws IOException, StorageException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); String newRecipeFileName = recipe.getName() + RECIPE_FILE_PREFIX; @@ -327,9 +322,14 @@ public void deleteRecipeFile(String recipeName) throws IOException { /** * Instantiates the given directory if it does not exist yet. */ - private void instantiateDirectoryIfNotExist(String directoryName) throws IOException { + private void instantiateDirectoryIfNotExist(String directoryName) throws IOException, StorageException { File outputDirectory = new File(directoryName); if (outputDirectory.exists()) { + if (!outputDirectory.isDirectory()) { + // throw error if not a valid directory. + String formattedErrorMessage = String.format(ErrorMessages.INVALID_DIRECTORY_ACCESS, outputDirectory); + throw new StorageException(formattedErrorMessage); + } return; } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index d25eff9707..1a328a4678 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -32,9 +32,7 @@ public class ErrorMessages { public static final String FILE_READ_ERROR_MESSAGE = "Something went wrong when reading from the file"; public static final String DIRECTORY_INSTANTIATION_FAILED_MESSAGE = "Failed to create the directory for "; public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; - public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = " failed to be loaded into Decodex"; + public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = "failed to be loaded into Decodex"; public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; - public static final String INVALID_RECIPE_DIRECTORY_FILETYPE = "The recipe folder does not seem " - + "to be a valid folder, but rather a file, please change it " - + "to ensure that you can save your recipes created properly"; + public static final String INVALID_DIRECTORY_ACCESS = "Failed to access %s directory"; } From 57defc0419e2af8a5d807f299d9732613800297e Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sun, 31 Oct 2021 12:59:02 +0800 Subject: [PATCH 405/607] Added the filtering of recipe files to also filter for presence of .txt prefix --- src/main/java/decodex/storage/Storage.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 3c4092278b..52aa8e286e 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -45,7 +45,6 @@ public class Storage { private static final String FILENAME_EXTENSION_SPLIT_REGEX = "[.]"; - /** * Specifies the index for the corresponding fields. */ @@ -60,7 +59,6 @@ public class Storage { /** * Initializes a new Storage. - * */ public Storage() { } @@ -114,6 +112,10 @@ private File[] getAllRecipeFiles() throws StorageException { File[] files = recipeDirectory.listFiles(); File[] recipeFiles = Arrays.stream(files) .filter(file -> file.isFile()) + .filter(file -> { + String fileName = file.getName(); + return fileName.contains(RECIPE_FILE_PREFIX); + }) .toArray(size -> new File[size]); return recipeFiles; From c1e89c090b41bf444167f050d565bb0e9c7ff12a Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 15:58:43 +0800 Subject: [PATCH 406/607] Changes as per suggested and some changes to the naming convention of the variable. --- src/main/java/decodex/storage/Storage.java | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 52aa8e286e..b7cffd2938 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -69,9 +69,8 @@ public Storage() { * @param moduleManager The ModuleManager object. * @param recipeManager The RecipeManager object. * @param ui The Ui object. - * @throws IOException If an error occurred when reading the file. - * @throws ModuleException If module parameters are invalid. - * @throws ModuleManagerException If provided module name is not an available module. + * @throws IOException If an error occurred when reading the file. + * @throws StorageException If the recipe directory is not a file. */ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager recipeManager, Ui ui) throws IOException, StorageException { @@ -106,10 +105,11 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager * * @return The list of recipe File objects. */ - private File[] getAllRecipeFiles() throws StorageException { + private File[] getAllRecipeFiles() { File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File[] files = recipeDirectory.listFiles(); + File[] recipeFiles = Arrays.stream(files) .filter(file -> file.isFile()) .filter(file -> { @@ -130,6 +130,7 @@ private File[] getAllRecipeFiles() throws StorageException { * @return The Recipe object. * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. + * @throws RecipeException If recipe name is not valid. */ private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, ModuleManager moduleManager) throws ModuleException, ModuleManagerException, RecipeException { @@ -160,6 +161,7 @@ private Recipe parseContentToRecipe(String recipeFileName, String recipeContent, * @throws IOException If an error occurred when reading the file. * @throws ModuleException If module parameters are invalid. * @throws ModuleManagerException If provided module name is not an available module. + * @throws RecipeException If recipe name is not valid. */ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleManager moduleManager) throws IOException, ModuleException, ModuleManagerException, RecipeException { @@ -178,6 +180,7 @@ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleM * @return The byte contents from the input file. * @throws IOException If an error occurred when reading the file or * the input file does not exist. + * @throws StorageException If the input directory is a file. */ public byte[] readInputFromFile(String fileName) throws IOException, StorageException { instantiateDirectoryIfNotExist(DEFAULT_INPUT_DIRECTORY); @@ -312,7 +315,7 @@ public void deleteRecipeFile(String recipeName) throws IOException { File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); - if (!outputRecipeFile.exists()) { + if (!outputRecipeFile.isFile()) { return; } @@ -323,20 +326,22 @@ public void deleteRecipeFile(String recipeName) throws IOException { /** * Instantiates the given directory if it does not exist yet. + * + * @throws IOException If the directory is a file. */ private void instantiateDirectoryIfNotExist(String directoryName) throws IOException, StorageException { - File outputDirectory = new File(directoryName); - if (outputDirectory.exists()) { - if (!outputDirectory.isDirectory()) { - // throw error if not a valid directory. - String formattedErrorMessage = String.format(ErrorMessages.INVALID_DIRECTORY_ACCESS, outputDirectory); - throw new StorageException(formattedErrorMessage); - } + File directory = new File(directoryName); + if (directory.isDirectory()) { return; } + if (directory.isFile()) { + // throw error if not a valid directory. + String formattedErrorMessage = String.format(ErrorMessages.INVALID_DIRECTORY_ACCESS, directory); + throw new StorageException(formattedErrorMessage); + } boolean isSuccessful; - isSuccessful = outputDirectory.mkdir(); + isSuccessful = directory.mkdir(); if (!isSuccessful) { throw new IOException(ErrorMessages.DIRECTORY_INSTANTIATION_FAILED_MESSAGE + directoryName); } From 63b1af3848be6f00e00fadb00f3c56852f4f3fda Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 16:10:54 +0800 Subject: [PATCH 407/607] Removed dead code for input output features that have been dropped for 2.1 --- .../commands/recipe/RecipeDeleteCommand.java | 3 +- src/main/java/decodex/storage/Storage.java | 73 ++----------------- .../decodex/ui/messages/ErrorMessages.java | 1 + 3 files changed, 9 insertions(+), 68 deletions(-) diff --git a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java index 1fb550f3ac..c631f8f7df 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java @@ -5,6 +5,7 @@ import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; @@ -36,7 +37,7 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec recipeManager.removeRecipe(recipeName); try { storage.deleteRecipeFile(recipeName); - } catch (IOException err) { + } catch (IOException | StorageException err) { ui.printError(err); } ui.printRecipeDeleted(recipeName); diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index b7cffd2938..902bc4a61d 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -18,8 +18,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; @@ -29,18 +27,13 @@ public class Storage { /** * Specifies the default directories for input, output and recipes respectively. */ - private static final String DEFAULT_INPUT_DIRECTORY = "input"; - private static final String DEFAULT_OUTPUT_DIRECTORY = "output"; private static final String DEFAULT_RECIPE_DIRECTORY = "recipe"; /** * Specifies the corresponding file prefixes. */ - private static final String DATA_FILE_PREFIX = ".data"; private static final String RECIPE_FILE_PREFIX = ".txt"; - private static final String OUTPUT_FILENAME_FORMAT = "yyyy-dd-MM__HH.mm.ss"; - private static final String LINE_BREAK_REGEX = "\\r?\\n"; private static final String FILENAME_EXTENSION_SPLIT_REGEX = "[.]"; @@ -109,7 +102,7 @@ private File[] getAllRecipeFiles() { File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File[] files = recipeDirectory.listFiles(); - + File[] recipeFiles = Arrays.stream(files) .filter(file -> file.isFile()) .filter(file -> { @@ -173,26 +166,6 @@ public Recipe readRecipeFromFile(String recipeFilename, File recipeFile, ModuleM return loadedRecipe; } - /** - * Reads and returns the input from the provided file. - * - * @param fileName The name of the input file specified by the user. - * @return The byte contents from the input file. - * @throws IOException If an error occurred when reading the file or - * the input file does not exist. - * @throws StorageException If the input directory is a file. - */ - public byte[] readInputFromFile(String fileName) throws IOException, StorageException { - instantiateDirectoryIfNotExist(DEFAULT_INPUT_DIRECTORY); - - File inputDirectory = new File(DEFAULT_INPUT_DIRECTORY); - File inputFile = new File(inputDirectory, fileName); - Path inputFilePath = inputFile.toPath(); - - byte[] inputContent = readContentFromFile(inputFilePath); - return inputContent; - } - /** * Reads and returns the contents from the file. * @@ -211,43 +184,6 @@ private byte[] readContentFromFile(Path readFilePath) throws IOException, FileNo } } - /** - * Saves the provided output into a file. - * - * @param outputBytes The encoded or decoded output. - * @throws IOException If an error occurred when creating the file - * or when writing to the file. - */ - public void saveOutputToFile(byte[] outputBytes) throws IOException, StorageException { - instantiateDirectoryIfNotExist(DEFAULT_OUTPUT_DIRECTORY); - - LocalDateTime currentDateTime = LocalDateTime.now(); - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(OUTPUT_FILENAME_FORMAT); - String formattedDateTime = currentDateTime.format(dateTimeFormatter); - String newOutputFileName = formattedDateTime + DATA_FILE_PREFIX; - - File outputDirectory = new File(DEFAULT_OUTPUT_DIRECTORY); - File outputFile = new File(outputDirectory, newOutputFileName); - saveBytesToFile(outputFile, outputBytes); - } - - /** - * Saves the content bytes into the provided file. - * - * @param writeFile The file to be saved to. - * @param contentBytes The bytes of the content to be saved. - * @throws IOException If an error occurred when creating the file - * or when writing to the file. - */ - private void saveBytesToFile(File writeFile, byte[] contentBytes) throws IOException { - try { - Path writeFilePath = writeFile.toPath(); - Files.write(writeFilePath, contentBytes); - } catch (IOException err) { - throw new IOException(ErrorMessages.FILE_WRITE_ERROR_MESSAGE); - } - } - /** * Saves the provided recipe into a file. * @@ -262,6 +198,9 @@ public void saveRecipeToFile(Recipe recipe) throws IOException, StorageException File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); + if (!outputRecipeFile.isFile()) { + throw new StorageException(ErrorMessages.INVALID_RECIPE_FILE); + } saveRecipeModulesToFile(outputRecipeFile, recipe); } @@ -309,14 +248,14 @@ private String formatModuleListForSaving(ArrayList modules) { * * @param recipeName The name of the deleted recipe. */ - public void deleteRecipeFile(String recipeName) throws IOException { + public void deleteRecipeFile(String recipeName) throws IOException, StorageException { String newRecipeFileName = recipeName + RECIPE_FILE_PREFIX; File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); if (!outputRecipeFile.isFile()) { - return; + throw new StorageException(ErrorMessages.INVALID_RECIPE_FILE); } if (!outputRecipeFile.delete()) { diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 1a328a4678..85259a994b 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -35,4 +35,5 @@ public class ErrorMessages { public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = "failed to be loaded into Decodex"; public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; public static final String INVALID_DIRECTORY_ACCESS = "Failed to access %s directory"; + public static final String INVALID_RECIPE_FILE = "The recipe file is not a valid file"; } From 4f4f2267560cd178543a306e398fec5ee398edc5 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 17:20:50 +0800 Subject: [PATCH 408/607] Updated to have show command for the original input. --- .../java/decodex/commands/HelpCommand.java | 3 +- .../java/decodex/commands/ShowCommand.java | 31 +++++++++++++ src/main/java/decodex/parser/Parser.java | 21 ++++++--- text-ui-test/EXPECTED.TXT | 45 +++++++++++++++++++ text-ui-test/input.txt | 2 + 5 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 src/main/java/decodex/commands/ShowCommand.java diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java index 5ae0a2cbd3..16b49ae7f0 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -24,7 +24,8 @@ public class HelpCommand extends Command { ExitCommand.COMMAND_HELP_MESSAGE, InputCommand.COMMAND_HELP_MESSAGE, ListCommand.COMMAND_HELP_MESSAGE, - SelectCommand.COMMAND_HELP_MESSAGE + SelectCommand.COMMAND_HELP_MESSAGE, + ShowCommand.COMMAND_HELP_MESSAGE }; private static final String[] RECIPE_COMMAND_HELP_MESSAGES = { diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java new file mode 100644 index 0000000000..fa2713d0db --- /dev/null +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -0,0 +1,31 @@ +package decodex.commands; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; + +public class ShowCommand extends Command { + public static final String COMMAND_WORD = "show"; + public static final String COMMAND_HELP_MESSAGE = "Shows your original input\n" + + "Syntax: show"; + + public ShowCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) throws DataManagerException, CommandException { + Data originalData = dataManager.getOriginalData(); + if (originalData == null) { + throw new CommandException(ErrorMessages.INPUT_EMPTY); + } + String originalDataInput = originalData.toString(); + + ui.printInput(originalDataInput); + } +} diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index e89a50d1b5..2fd3a2b924 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -2,13 +2,7 @@ import java.util.Arrays; -import decodex.commands.Command; -import decodex.commands.ExitCommand; -import decodex.commands.InputCommand; -import decodex.commands.ListCommand; -import decodex.commands.ResetCommand; -import decodex.commands.SelectCommand; -import decodex.commands.HelpCommand; +import decodex.commands.*; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; import decodex.ui.messages.ErrorMessages; @@ -132,6 +126,9 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc case HelpCommand.COMMAND_WORD: command = prepareHelpCommand(); break; + case ShowCommand.COMMAND_WORD: + command = prepareShowCommand(); + break; case RECIPE_COMMAND_WORD: command = prepareRecipeSubcommands(userInput); break; @@ -235,6 +232,16 @@ private SelectCommand prepareSelectCommand(String userInput) throws CommandExcep return new SelectCommand(selectCategory, itemName, parameters); } + /** + * Shows the original input data. + * + * @return The ShowCommand object. + * @throws CommandException If the original data input is empty. + */ + private ShowCommand prepareShowCommand() { + return new ShowCommand(); + } + /** * Prepares and returns recipe subcommands using the specified user input. * diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index e950698c43..76fb638a57 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -4,6 +4,51 @@ Welcome to Decodex, transforming data with ease Decodex [] > [x] Unknown command, please enter a valid command Decodex [] > [x] Unknown command, please enter a valid command Decodex [] > [x] Your command cannot be blank +Decodex [] > Syntax: < > - mandatory, { } - optional + +Here are the basic commands: +-> Shows list of available commands + Syntax: help + +-> Exit the program + Syntax: exit + +-> Input of data + Syntax: input + +-> List available modules and recipes + Syntax: list {category} + Acceptable values of category: modules, recipes + +-> Selects a module or recipe to run + Syntax: select module {moduleArgument}, select recipe + +-> Shows your original input + Syntax: show + +Here are the recipe commands: +-> Create new recipe + Syntax: recipe new + +-> List modules in recipe + Syntax: recipe list {recipeName} + +-> Select recipe for editing + Syntax: recipe select + +-> Add module to recipe + Syntax: recipe push {moduleArgument} + +-> Remove module from recipe + Syntax: recipe pop + +-> Clear all modules from recipe + Syntax: recipe reset + +-> Delete recipe + Syntax: recipe delete + +Decodex [] > [+] Input: "I am a lab rat" Decodex [] > [+] Input: "I am a lab rat" Decodex [] > [+] Output: "SSBhbSBhIGxhYiByYXQ=" Decodex [] > [+] Output: "I am a lab rat" diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index b88b402e08..7f847b244c 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,7 +1,9 @@ whoami bring me thanos +help input I am a lab rat +show select module base64encode select module base64decode select module hexencode From c67d5280ea21a47bb182252767c07a8d800742fd Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:23:28 +0800 Subject: [PATCH 409/607] Update error message for invalid offset for `RotEncode` --- src/main/java/decodex/modules/ModuleManager.java | 2 +- src/main/java/decodex/ui/messages/ErrorMessages.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/ModuleManager.java b/src/main/java/decodex/modules/ModuleManager.java index fef1663bc1..e29f2c5d10 100644 --- a/src/main/java/decodex/modules/ModuleManager.java +++ b/src/main/java/decodex/modules/ModuleManager.java @@ -189,7 +189,7 @@ private Module prepareRotEncoder(String[] parameters) throws ModuleException { int rotOffset = Integer.parseInt(parameters[MODULE_PARAMETER_INDEX]); return new RotEncoder(rotOffset); } catch (NumberFormatException err) { - throw new ModuleException(ErrorMessages.INVALID_MODULE_PARAMETERS); + throw new ModuleException(ErrorMessages.INVALID_ROTENCODE_PARAMETER); } } } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index afe6dee560..c00735606e 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -22,7 +22,8 @@ public class ErrorMessages { public static final String TOO_MANY_COMMAND_ARGUMENTS = "Too many command arguments"; public static final String INVALID_LIST_CATEGORY = "Invalid list category"; public static final String UNKNOWN_MODULE_NAME = "Unknown module name"; - public static final String INVALID_MODULE_PARAMETERS = "Invalid module parameters"; + public static final String INVALID_ROTENCODE_PARAMETER = "The rotation offset must be between -2147483648 (MIN_INT)" + + " and 2147483647 (MAX_INT)"; public static final String MISSING_MODULE_PARAMETERS = "Missing module parameters"; public static final String TOO_MANY_MODULE_PARAMETERS = "Too many module parameters"; public static final String UNKNOWN_SELECTION_CATEGORY = "Unknown selection category"; From 22097228e703333747ba6ce2f59b07c9508c1adf Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:25:25 +0800 Subject: [PATCH 410/607] Update UG with table specifying `rotencode` parameters --- docs/UserGuide.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d5eb0f8aeb..ca657b7c60 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -97,6 +97,12 @@ This section serves to help you better understand the terminologies used in this 4. Rotational Cipher 1. `rotencode` - Rotates alphabetical characters by a specified integer offset +| Module | Description | Parameters | +| -------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `base64encode` | Encodes the data using base64 format | N.A | +| `rotencode` | Rotates alphabetical characters by a specified integer offset | 1. `offset`: The rotation offset.

Must be between -2147483648 (MIN_INT) and 2147483647 (MAX_INT) | + + > :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. ## Features From 722b3504513850c9094859c80597436802d57c2d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 17:34:25 +0800 Subject: [PATCH 411/607] Additional handling to fix #263 --- src/main/java/decodex/commands/SelectCommand.java | 4 ++++ src/main/java/decodex/parser/Parser.java | 2 +- src/main/java/decodex/ui/messages/ErrorMessages.java | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index b8abc22288..ef9fb40eee 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -96,6 +96,10 @@ private void runRecipe(DataManager dataManager, RecipeManager recipeManager, Ui throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } + if (parameters.length > 0) { + throw new CommandException(ErrorMessages.ADDITIONAL_PARAMETER_AFTER_RECIPE_NAME_MESSAGE); + } + Recipe recipe = recipeManager.getRecipe(itemName); Data newData = recipe.run(dataManager.getCurrentData()); dataManager.setCurrentData(newData); diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 2fd3a2b924..a9ef82562f 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -217,7 +217,7 @@ private ResetCommand prepareResetCommand() { * * @param userInput The user input specified by the user. * @return The SelectCommand object. - * @throws CommandException If the number of command arguments is invalid. + * @throws CommandException If the number of command arguments is invalid.in */ private SelectCommand prepareSelectCommand(String userInput) throws CommandException { String[] tokens = getTokens(userInput); diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index afe6dee560..5f23bbe303 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -34,4 +34,6 @@ public class ErrorMessages { public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = " failed to be loaded into Decodex"; public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; + public static final String ADDITIONAL_PARAMETER_AFTER_RECIPE_NAME_MESSAGE = + "Additional argument found after recipe name"; } From 700709c6d64f3fb6666ed5fe1ad9d0702c565807 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 1 Nov 2021 17:37:40 +0800 Subject: [PATCH 412/607] Set recipeName as optional argument in `recipe lists` command Fixes #269 --- docs/UserGuide.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d5eb0f8aeb..20dbb32e3f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -215,7 +215,9 @@ Example: Show all the modules in a recipe, in the order they were added. -Format: `recipe list ` +Format: `recipe list {recipeName>}` + +> :information_source: When `recipeName` is not specified, modules from the recipe that is currently being edited will be listed instead. Example: From aa9c9ba0fac2b10bc2749e43d11ce2e9b4d2132a Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 17:41:22 +0800 Subject: [PATCH 413/607] Some minor changes, forgot to checkstyle... --- src/main/java/decodex/commands/ShowCommand.java | 3 ++- src/main/java/decodex/parser/Parser.java | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index fa2713d0db..b319fd3b6e 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -19,7 +19,8 @@ public ShowCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) throws DataManagerException, CommandException { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) + throws DataManagerException, CommandException { Data originalData = dataManager.getOriginalData(); if (originalData == null) { throw new CommandException(ErrorMessages.INPUT_EMPTY); diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index a9ef82562f..f6baa2d828 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -2,7 +2,14 @@ import java.util.Arrays; -import decodex.commands.*; +import decodex.commands.Command; +import decodex.commands.ExitCommand; +import decodex.commands.InputCommand; +import decodex.commands.HelpCommand; +import decodex.commands.ListCommand; +import decodex.commands.ResetCommand; +import decodex.commands.SelectCommand; +import decodex.commands.ShowCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; import decodex.ui.messages.ErrorMessages; @@ -217,7 +224,7 @@ private ResetCommand prepareResetCommand() { * * @param userInput The user input specified by the user. * @return The SelectCommand object. - * @throws CommandException If the number of command arguments is invalid.in + * @throws CommandException If the number of command arguments is invalid. */ private SelectCommand prepareSelectCommand(String userInput) throws CommandException { String[] tokens = getTokens(userInput); @@ -239,7 +246,7 @@ private SelectCommand prepareSelectCommand(String userInput) throws CommandExcep * @throws CommandException If the original data input is empty. */ private ShowCommand prepareShowCommand() { - return new ShowCommand(); + return new ShowCommand(); } /** From 47a661df6a08f3ce125eba4964321aa781c49e96 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:42:37 +0800 Subject: [PATCH 414/607] Update error message --- src/main/java/decodex/ui/messages/ErrorMessages.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index c00735606e..0ff1ce7b7c 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -22,8 +22,8 @@ public class ErrorMessages { public static final String TOO_MANY_COMMAND_ARGUMENTS = "Too many command arguments"; public static final String INVALID_LIST_CATEGORY = "Invalid list category"; public static final String UNKNOWN_MODULE_NAME = "Unknown module name"; - public static final String INVALID_ROTENCODE_PARAMETER = "The rotation offset must be between -2147483648 (MIN_INT)" - + " and 2147483647 (MAX_INT)"; + public static final String INVALID_ROTENCODE_PARAMETER = "The rotation offset must be an integer between " + + "-2147483648 (MIN_INT) and 2147483647 (MAX_INT)"; public static final String MISSING_MODULE_PARAMETERS = "Missing module parameters"; public static final String TOO_MANY_MODULE_PARAMETERS = "Too many module parameters"; public static final String UNKNOWN_SELECTION_CATEGORY = "Unknown selection category"; From f995b7c0b4a81ea42617216181aa635ac3c8d287 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:42:51 +0800 Subject: [PATCH 415/607] Update UG --- docs/UserGuide.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index ca657b7c60..91535e33f8 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -85,6 +85,8 @@ This section serves to help you better understand the terminologies used in this ## List of Available Modules +> :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. + 1. Base64 1. `base64encode` - Encodes the data using base64 format 2. `base64decode` - Decodes the data using base64 format @@ -97,13 +99,10 @@ This section serves to help you better understand the terminologies used in this 4. Rotational Cipher 1. `rotencode` - Rotates alphabetical characters by a specified integer offset -| Module | Description | Parameters | -| -------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| `base64encode` | Encodes the data using base64 format | N.A | -| `rotencode` | Rotates alphabetical characters by a specified integer offset | 1. `offset`: The rotation offset.

Must be between -2147483648 (MIN_INT) and 2147483647 (MAX_INT) | - - -> :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. +| Module | Description | `moduleArgument` | +| -------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| `base64encode` | Encodes the data using base64 format. | N.A | +| `rotencode` | Rotates alphabetical characters by a specified integer offset.

E.g., `select module rotencode 13` | The rotation offset.

Must be an integer between -2147483648 (MIN_INT) and 2147483647 (MAX_INT). | ## Features From 406bdd55e0d38b25151d21c52d6b2eafd763fe14 Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 1 Nov 2021 17:45:41 +0800 Subject: [PATCH 416/607] Update product.txt --- docs/carbon/product.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/carbon/product.txt b/docs/carbon/product.txt index a8d86dbaac..2fa445cc61 100644 --- a/docs/carbon/product.txt +++ b/docs/carbon/product.txt @@ -1,10 +1,10 @@ Decodex [] > input NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk= [+] Input: "NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=" -Decodex [] > select base64decode +Decodex [] > select module base64decode [+] Output: "5131524765304e544d6a45784d315239" -Decodex [] > select hexdecode +Decodex [] > select module hexdecode [+] Output: "Q1RGe0NTMjExM1R9" -Decodex [] > select base64decode +Decodex [] > select module base64decode [+] Output: "CTF{CS2113T}" -https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520input%2520NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk%253D%250A%255B%252B%255D%2520Input%253A%2520%2522NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk%253D%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520base64decode%250A%255B%252B%255D%2520Output%253A%2520%25225131524765304e544d6a45784d315239%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520hexdecode%250A%255B%252B%255D%2520Output%253A%2520%2522Q1RGe0NTMjExM1R9%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520base64decode%250A%255B%252B%255D%2520Output%253A%2520%2522CTF%257BCS2113T%257D%2522 \ No newline at end of file +https://carbon.now.sh/?bg=rgba%28255%2C255%2C255%2C1%29&t=base16-dark&wt=none&l=text&ds=false&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=Decodex%2520%255B%255D%2520%253E%2520input%2520NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk%253D%250A%255B%252B%255D%2520Input%253A%2520%2522NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk%253D%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520module%2520base64decode%250A%255B%252B%255D%2520Output%253A%2520%25225131524765304e544d6a45784d315239%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520module%2520hexdecode%250A%255B%252B%255D%2520Output%253A%2520%2522Q1RGe0NTMjExM1R9%2522%250ADecodex%2520%255B%255D%2520%253E%2520select%2520module%2520base64decode%250A%255B%252B%255D%2520Output%253A%2520%2522CTF%257BCS2113T%257D%2522 From 4e93562df72c09c218381a57afc30270b08a4ccf Mon Sep 17 00:00:00 2001 From: rizemon Date: Mon, 1 Nov 2021 17:49:10 +0800 Subject: [PATCH 417/607] Update product image --- docs/images/carbon(6).png | Bin 53359 -> 49764 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/carbon(6).png b/docs/images/carbon(6).png index e52224c7f086eb2f13f6312bf25242efec15f30d..150db708bb08efaf755ad6179ec80bf887467056 100644 GIT binary patch literal 49764 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 z≥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 Date: Mon, 1 Nov 2021 17:56:15 +0800 Subject: [PATCH 418/607] Update Command Summary for recipe list command --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 20dbb32e3f..db9889481e 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -292,7 +292,7 @@ Coming soon to a cinema near you in the future*…* | Reset to original data | `reset` | `reset` | | Create new recipe | `recipe new ` | `recipe new testRecipe` | | Select recipe for editing | `recipe select ` | `recipe select testRecipe` | -| List modules in recipe | `recipe list ` | `recipe list testRecipe` | +| List modules in recipe | `recipe list {recipeName}` | `recipe list testRecipe` | | Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | | Remove module from recipe | `recipe pop` | `recipe pop` | | Clear all modules in recipe | `recipe reset` | `recipe reset` | From 5be54a2ec606d688076d5b45ecd1b1d715d5485d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 17:57:19 +0800 Subject: [PATCH 419/607] Updated based on most recent upstream (forgot Im working on another device) --- src/main/java/decodex/commands/ShowCommand.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index b319fd3b6e..528cf728f0 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -6,6 +6,7 @@ import decodex.data.exception.DataManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; @@ -19,14 +20,13 @@ public ShowCommand() { } @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws DataManagerException, CommandException { - Data originalData = dataManager.getOriginalData(); - if (originalData == null) { + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws DataManagerException, CommandException { + Data currentData = dataManager.getCurrentData(); + if (currentData == null) { throw new CommandException(ErrorMessages.INPUT_EMPTY); } - String originalDataInput = originalData.toString(); - - ui.printInput(originalDataInput); + String currentDataInput = currentData.toString(); + ui.printInput(currentDataInput); } } From 8575d3bd63a2e6a9ae2475a7518a5333f67eb59b Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 18:05:31 +0800 Subject: [PATCH 420/607] Addtionally handling to make sure saving of recipes to file are done only one FILES. --- src/main/java/decodex/storage/Storage.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 902bc4a61d..f62d11ce61 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -198,9 +198,6 @@ public void saveRecipeToFile(Recipe recipe) throws IOException, StorageException File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File outputRecipeFile = new File(recipeDirectory, newRecipeFileName); - if (!outputRecipeFile.isFile()) { - throw new StorageException(ErrorMessages.INVALID_RECIPE_FILE); - } saveRecipeModulesToFile(outputRecipeFile, recipe); } @@ -212,7 +209,11 @@ public void saveRecipeToFile(Recipe recipe) throws IOException, StorageException * @throws IOException If an error occurred when creating the file * or when writing to the file. */ - private void saveRecipeModulesToFile(File writeFile, Recipe recipeToBeSaved) throws IOException { + private void saveRecipeModulesToFile(File writeFile, Recipe recipeToBeSaved) throws IOException, StorageException { + // To ensure only writes to file happen. + if (writeFile.isDirectory()) { + throw new StorageException(ErrorMessages.INVALID_RECIPE_FILE); + } ArrayList modules = recipeToBeSaved.getModuleList(); try { From 9a36d9de899a8c9a03f4117e1f372acf619b6bc7 Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:17:20 +0800 Subject: [PATCH 421/607] Added a line of clarification for moduleArgument Added a line of clarification for reader that only rotencode takes in the moduleArgument. Fixes #260 --- docs/UserGuide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index db9889481e..7e476ba89c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -163,6 +163,8 @@ Format: `select module {moduleArgument}`, `select recipe :information_source: `recipeName` is the name of an available recipe that is loaded by the program. +> :exclamation: Currently, `moduleArgument` is needed when using `rotencode` module, the rest of the currently available modules do not need the `moduleArgument`. + Examples: - `select module base64decode` Runs the `base64decode` module on the current data. From aa087a52bac6f593d936beed5f2c5b9097090edf Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:19:47 +0800 Subject: [PATCH 422/607] Addition rephrase to prev UG commit Just some additional rephrasing to make it sound more specific and less ambiguous. --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7e476ba89c..4e4fe10ad6 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -163,7 +163,7 @@ Format: `select module {moduleArgument}`, `select recipe :information_source: `recipeName` is the name of an available recipe that is loaded by the program. -> :exclamation: Currently, `moduleArgument` is needed when using `rotencode` module, the rest of the currently available modules do not need the `moduleArgument`. +> :exclamation: Currently, the `moduleArgument` is needed **only** when using the `rotencode` module, the rest of the currently available modules do not need the `moduleArgument`. Examples: From a058370f1b45ab0280c89e59c4aa909f87ab1dfc Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 18:25:53 +0800 Subject: [PATCH 423/607] Added suggested changes --- src/main/java/decodex/commands/SelectCommand.java | 2 +- src/main/java/decodex/commands/ShowCommand.java | 4 ++-- src/main/java/decodex/ui/messages/ErrorMessages.java | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index ed489b4417..0f6b06218b 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -99,7 +99,7 @@ private void runRecipe(DataManager dataManager, RecipeManager recipeManager, Ui } if (parameters.length > 0) { - throw new CommandException(ErrorMessages.ADDITIONAL_PARAMETER_AFTER_RECIPE_NAME_MESSAGE); + throw new CommandException(ErrorMessages.TOO_MANY_ARGUMENTS_MESSAGE); } Recipe recipe = recipeManager.getRecipe(itemName); diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index 528cf728f0..f35d3b62e2 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -12,7 +12,7 @@ public class ShowCommand extends Command { public static final String COMMAND_WORD = "show"; - public static final String COMMAND_HELP_MESSAGE = "Shows your original input\n" + public static final String COMMAND_HELP_MESSAGE = "Shows the current data\n" + "Syntax: show"; public ShowCommand() { @@ -27,6 +27,6 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec throw new CommandException(ErrorMessages.INPUT_EMPTY); } String currentDataInput = currentData.toString(); - ui.printInput(currentDataInput); + ui.printOutput(currentDataInput); } } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 5f23bbe303..456c8ca924 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -34,6 +34,5 @@ public class ErrorMessages { public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = " failed to be loaded into Decodex"; public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; - public static final String ADDITIONAL_PARAMETER_AFTER_RECIPE_NAME_MESSAGE = - "Additional argument found after recipe name"; + public static final String TOO_MANY_ARGUMENTS_MESSAGE = "Too many arguments"; } From 0acbc029641e40c1c48872009634dad641f66129 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:26:28 +0800 Subject: [PATCH 424/607] Add `RecipeDeselectCommand` --- .../java/decodex/commands/HelpCommand.java | 2 + .../recipe/RecipeDeselectCommand.java | 36 +++++++++++ .../decodex/parser/RecipeCommandParser.java | 16 +++++ .../java/decodex/recipes/RecipeManager.java | 9 ++- src/main/java/decodex/ui/Ui.java | 6 ++ .../decodex/ui/messages/RegularMessages.java | 1 + .../commands/RecipeDeselectCommandTest.java | 64 +++++++++++++++++++ 7 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java create mode 100644 src/test/java/decodex/commands/RecipeDeselectCommandTest.java diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java index aa3f478d43..481c0a0c46 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -1,6 +1,7 @@ package decodex.commands; import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.commands.recipe.RecipeDeselectCommand; import decodex.commands.recipe.RecipeListCommand; import decodex.commands.recipe.RecipeNewCommand; import decodex.commands.recipe.RecipePopCommand; @@ -32,6 +33,7 @@ public class HelpCommand extends Command { RecipeNewCommand.COMMAND_HELP_MESSAGE, RecipeListCommand.COMMAND_HELP_MESSAGE, RecipeSelectCommand.COMMAND_HELP_MESSAGE, + RecipeDeselectCommand.COMMAND_HELP_MESSAGE, RecipePushCommand.COMMAND_HELP_MESSAGE, RecipePopCommand.COMMAND_HELP_MESSAGE, RecipeResetCommand.COMMAND_HELP_MESSAGE, diff --git a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java new file mode 100644 index 0000000000..072e2a5f2a --- /dev/null +++ b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java @@ -0,0 +1,36 @@ +package decodex.commands.recipe; + +import java.io.IOException; + +import decodex.commands.Command; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; + +public class RecipeDeselectCommand extends Command { + + public static final String COMMAND_WORD = "deselect"; + public static final String COMMAND_HELP_MESSAGE = "Deselect editing recipe\n" + + "Syntax: recipe deselect"; + + public RecipeDeselectCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, ModuleManagerException, DataManagerException, ModuleException, + RecipeException, RecipeManagerException, IOException { + String recipeName = recipeManager.getEditingRecipe().getName(); + recipeManager.deselectEditingRecipe(); + ui.printRecipeDeselected(recipeName); + } +} diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index 6818ee5783..a2d6a31d73 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -4,6 +4,7 @@ import decodex.commands.Command; import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.commands.recipe.RecipeDeselectCommand; import decodex.commands.recipe.RecipeNewCommand; import decodex.commands.recipe.RecipePopCommand; import decodex.commands.recipe.RecipePushCommand; @@ -55,6 +56,9 @@ public Command parseCommand(String userInput) throws CommandException { case RecipeSelectCommand.COMMAND_WORD: recipeCommand = prepareRecipeSelectCommand(subCommandTokens); break; + case RecipeDeselectCommand.COMMAND_WORD: + recipeCommand = prepareRecipeDeselectCommand(subCommandTokens); + break; case RecipePushCommand.COMMAND_WORD: recipeCommand = prepareRecipePushCommand(subCommandTokens); break; @@ -117,6 +121,18 @@ private RecipeSelectCommand prepareRecipeSelectCommand(String[] subCommandTokens return new RecipeSelectCommand(recipeName); } + /** + * Prepares and returns the RecipeDeselectCommand. + * + * @param subCommandTokens The list of recipe subcommand tokens. + * @return The RecipeDeselectCommand object. + * @throws CommandException If the number of arguments is invalid. + */ + private RecipeDeselectCommand prepareRecipeDeselectCommand(String[] subCommandTokens) throws CommandException { + checkValidNumberOfArguments(subCommandTokens, SUBCOMMAND_NO_ARGUMENT_LENGTH); + return new RecipeDeselectCommand(); + } + /** * Prepares and returns the RecipePushCommand. * diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index e6adf78e37..b15db83ed4 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -1,6 +1,5 @@ package decodex.recipes; -import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Set; @@ -124,6 +123,14 @@ public void selectRecipeForEditing(String name) throws RecipeManagerException { logger.fine(String.format("[RecipeManager] Selected recipe %s", name)); } + /** + * Deselects the current editing recipe. + */ + public void deselectEditingRecipe() { + logger.fine(String.format("[RecipeManager] Deselected recipe %s", editingRecipeName)); + editingRecipeName = null; + } + /** * Appends a new module to the end of the recipe currently being edited. * diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index c8a3b401a2..8ece377438 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -179,6 +179,12 @@ public void printRecipeSelected(String recipeName) { printSuccess(String.format(RegularMessages.RECIPE_SELECT, recipeName)); } + public void printRecipeDeselected(String recipeName) { + assert recipeName != null : "Recipe name should not be null"; + assert !recipeName.isBlank() : "Recipe name should not be empty"; + printSuccess(String.format(RegularMessages.RECIPE_DESELECT, recipeName)); + } + /** * Prints a message with a success status icon. * diff --git a/src/main/java/decodex/ui/messages/RegularMessages.java b/src/main/java/decodex/ui/messages/RegularMessages.java index b49799cad6..4f6b85c990 100644 --- a/src/main/java/decodex/ui/messages/RegularMessages.java +++ b/src/main/java/decodex/ui/messages/RegularMessages.java @@ -17,6 +17,7 @@ public class RegularMessages { public static final String REMOVED_RECIPE_FROM_RECIPE = "Removed %s module from recipe %s"; public static final String ADDED_MODULE_TO_RECIPE = "Added %s module to recipe %s"; public static final String RECIPE_SELECT = "Recipe %s selected for editing"; + public static final String RECIPE_DESELECT = "Recipe %s deselected"; public static final String RECIPE_EMPTY = "There are no modules in the recipe"; public static final String RECIPE_MODULES_EXECUTED = "Modules executed:"; public static final String BASIC_COMMANDS = "Here are the basic commands:"; diff --git a/src/test/java/decodex/commands/RecipeDeselectCommandTest.java b/src/test/java/decodex/commands/RecipeDeselectCommandTest.java new file mode 100644 index 0000000000..72c11a64b4 --- /dev/null +++ b/src/test/java/decodex/commands/RecipeDeselectCommandTest.java @@ -0,0 +1,64 @@ +package decodex.commands; + +import java.io.IOException; + +import decodex.commands.recipe.RecipeDeselectCommand; +import decodex.commands.recipe.RecipeSelectCommand; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class RecipeDeselectCommandTest { + + @Test + public void run_deselectEditingRecipe_successExpectRecipeManagerException() throws RecipeException, + RecipeManagerException, CommandException, IOException, ModuleException, ModuleManagerException, + DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + // Add recipe + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + // Select recipe + RecipeSelectCommand recipeSelectCommand = new RecipeSelectCommand(testRecipeName); + recipeSelectCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + + // Deselect recipe + RecipeDeselectCommand recipeDeselectCommand = new RecipeDeselectCommand(); + recipeDeselectCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + + assertThrows(RecipeManagerException.class, () -> recipeManager.getEditingRecipe()); + } + + @Test + public void run_noSelectedRecipe_expectRecipeManagerException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + // Deselect recipe + RecipeDeselectCommand recipeDeselectCommand = new RecipeDeselectCommand(); + assertThrows(RecipeManagerException.class, () -> recipeDeselectCommand.run(dataManager, moduleManager, ui, + recipeManager, storage)); + } +} From f996a7774e158c27ca3da468b584fc714ce83624 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 18:30:51 +0800 Subject: [PATCH 425/607] Removed redundant check, since the datamanager throws the same type of error when the currentData to be returned is null. --- src/main/java/decodex/commands/ShowCommand.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index f35d3b62e2..38967ec7ab 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -2,13 +2,11 @@ import decodex.data.Data; import decodex.data.DataManager; -import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import decodex.ui.messages.ErrorMessages; public class ShowCommand extends Command { public static final String COMMAND_WORD = "show"; @@ -19,13 +17,20 @@ public ShowCommand() { super(); } + /** + * Shows the current data. + * + * @param dataManager The DataManager object. + * @param moduleManager The ModuleManager object. + * @param ui The Ui object. + * @param recipeManager The RecipeManager object. + * @param storage The Storage object. + * @throws DataManagerException If the current data is empty. + */ @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws DataManagerException, CommandException { + Storage storage) throws DataManagerException { Data currentData = dataManager.getCurrentData(); - if (currentData == null) { - throw new CommandException(ErrorMessages.INPUT_EMPTY); - } String currentDataInput = currentData.toString(); ui.printOutput(currentDataInput); } From a9fa60dbae5ee8260457fc90ef2ddb5807a43622 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:34:41 +0800 Subject: [PATCH 426/607] Update UG with `recipe deselect` --- docs/UserGuide.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d5eb0f8aeb..c34a595eaa 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -27,6 +27,7 @@ This guide serves to help you understand the usage of the program to encode and - [Recipe Commands: `recipe`](#recipe-commands-recipe) - [Create a New Recipe: `recipe new`](#create-a-new-recipe-recipe-new) - [Select a Recipe for Editing: `recipe select`](#select-a-recipe-for-editing-recipe-select) + - [Deselect a Recipe from Editing: `recipe deselect`](#deselect-a-recipe-from-editing-recipe-deselect) - [List Modules in a Recipe: `recipe list`](#list-modules-in-a-recipe-recipe-list) - [Add a Module to a Recipe: `recipe push`](#add-a-module-to-a-recipe-recipe-push) - [Remove a Module from a Recipe: `recipe pop`](#remove-a-module-from-a-recipe-recipe-pop) @@ -211,6 +212,18 @@ Example: ![recipeSelect.png](images/recipeSelect.png) +#### Deselect a Recipe from Editing: `recipe deselect` + +Deselects the current recipe from being edited. + +Format: `recipe deselect` + +Example: + +- `recipe deselect` Deselects the current recipe from being edited. + +![recipeDeselect.png](images/recipeDeselect.png) + #### List Modules in a Recipe: `recipe list` Show all the modules in a recipe, in the order they were added. From 45f7998a121adfacde9cd46aff6e26b51ce849cf Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 18:39:16 +0800 Subject: [PATCH 427/607] Fixed the text ui test and made changes to the output to show `Current:` instead, when doing a `show` command --- src/main/java/decodex/commands/ShowCommand.java | 2 +- src/main/java/decodex/ui/Ui.java | 6 ++++++ text-ui-test/EXPECTED.TXT | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index 38967ec7ab..389b990bdb 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -32,6 +32,6 @@ public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, Rec Storage storage) throws DataManagerException { Data currentData = dataManager.getCurrentData(); String currentDataInput = currentData.toString(); - ui.printOutput(currentDataInput); + ui.printCurrentData(currentDataInput); } } diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index c8a3b401a2..244c13948e 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -19,6 +19,7 @@ public class Ui { private static final String ERROR_ICON = "[x]"; private static final String INPUT_PREFIX = "Input:"; private static final String OUTPUT_PREFIX = "Output:"; + private static final String CURRENT_PREFIX = "Current:"; private final Scanner in; @@ -264,4 +265,9 @@ public void printCommandHelp(String[] basicCommandHelpList, String[] recipeComma System.out.printf("-> %s\n\n", commandFormat.replace("\n", "\n ")); } } + + public void printCurrentData(String currentData) { + assert currentData != null : "Current data should not be null"; + printSuccess(String.format("%s \"%s\"", CURRENT_PREFIX, currentData)); + } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 76fb638a57..6a7750e043 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -23,7 +23,7 @@ Here are the basic commands: -> Selects a module or recipe to run Syntax: select module {moduleArgument}, select recipe --> Shows your original input +-> Shows the current data Syntax: show Here are the recipe commands: @@ -49,7 +49,7 @@ Here are the recipe commands: Syntax: recipe delete Decodex [] > [+] Input: "I am a lab rat" -Decodex [] > [+] Input: "I am a lab rat" +Decodex [] > [+] Current: "I am a lab rat" Decodex [] > [+] Output: "SSBhbSBhIGxhYiByYXQ=" Decodex [] > [+] Output: "I am a lab rat" Decodex [] > [+] Output: "4920616d2061206c616220726174" From 11450fb9dd7878f6dcb92979a9c505e93a9438b9 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:40:04 +0800 Subject: [PATCH 428/607] Update UG command description and command summary --- docs/UserGuide.md | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c34a595eaa..809c61f9f2 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -27,7 +27,7 @@ This guide serves to help you understand the usage of the program to encode and - [Recipe Commands: `recipe`](#recipe-commands-recipe) - [Create a New Recipe: `recipe new`](#create-a-new-recipe-recipe-new) - [Select a Recipe for Editing: `recipe select`](#select-a-recipe-for-editing-recipe-select) - - [Deselect a Recipe from Editing: `recipe deselect`](#deselect-a-recipe-from-editing-recipe-deselect) + - [Deselect the Current Editing Recipe: `recipe deselect`](#deselect-the-current-editing-recipe-recipe-deselect) - [List Modules in a Recipe: `recipe list`](#list-modules-in-a-recipe-recipe-list) - [Add a Module to a Recipe: `recipe push`](#add-a-module-to-a-recipe-recipe-push) - [Remove a Module from a Recipe: `recipe pop`](#remove-a-module-from-a-recipe-recipe-pop) @@ -212,15 +212,15 @@ Example: ![recipeSelect.png](images/recipeSelect.png) -#### Deselect a Recipe from Editing: `recipe deselect` +#### Deselect the Current Editing Recipe: `recipe deselect` -Deselects the current recipe from being edited. +Deselects the current editing recipe. Format: `recipe deselect` Example: -- `recipe deselect` Deselects the current recipe from being edited. +- `recipe deselect` Deselects the current editing recipe. ![recipeDeselect.png](images/recipeDeselect.png) @@ -295,20 +295,21 @@ Coming soon to a cinema near you in the future*…* ## Command Summary -| Action | Command Format | Example Usage | -|---------------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------| -| Enter input data | `input ` | `input HelloWorld!` | -| List available modules/recipe | `list {category}` | `list` `list module`
`list recipe` | -| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | -| Reset to original data | `reset` | `reset` | -| Create new recipe | `recipe new ` | `recipe new testRecipe` | -| Select recipe for editing | `recipe select ` | `recipe select testRecipe` | -| List modules in recipe | `recipe list ` | `recipe list testRecipe` | -| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | -| Remove module from recipe | `recipe pop` | `recipe pop` | -| Clear all modules in recipe | `recipe reset` | `recipe reset` | -| Delete recipe | `recipe delete ` | `recipe delete testRecipe` | -| Exit program | `exit` | `exit` | +| Action | Command Format | Example Usage | +| ------------------------------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| Enter input data | `input ` | `input HelloWorld!` | +| List available modules/recipe | `list {category}` | `list` `list module`
`list recipe` | +| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | +| Reset to original data | `reset` | `reset` | +| Create new recipe | `recipe new ` | `recipe new testRecipe` | +| Select recipe for editing | `recipe select ` | `recipe select testRecipe` | +| Deselects the current editing recipe | `recipe deselect` | `recipe deselect` | +| List modules in recipe | `recipe list ` | `recipe list testRecipe` | +| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | +| Remove module from recipe | `recipe pop` | `recipe pop` | +| Clear all modules in recipe | `recipe reset` | `recipe reset` | +| Delete recipe | `recipe delete ` | `recipe delete testRecipe` | +| Exit program | `exit` | `exit` | ## FAQ From 2bfe7ea4e484f9e8d69ccaa5f69952cc00fd7afe Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:48:34 +0800 Subject: [PATCH 429/607] Made changes to link user Link user to select module examples in command summary. Fixed the TOC linking to one section And also remove one '>' --- docs/UserGuide.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index f2d686cd9c..43f7ced141 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -28,7 +28,7 @@ This guide serves to help you understand the usage of the program to encode and - [Create a New Recipe: `recipe new`](#create-a-new-recipe-recipe-new) - [Select a Recipe for Editing: `recipe select`](#select-a-recipe-for-editing-recipe-select) - [List Modules in a Recipe: `recipe list`](#list-modules-in-a-recipe-recipe-list) - - [Add a Module to a Recipe: `recipe push`](#add-a-module-to-a-recipe-recipe-push) + - [Add a Module to a Recipe: `recipe push`](#add-a-module-into-a-recipe-recipe-push) - [Remove a Module from a Recipe: `recipe pop`](#remove-a-module-from-a-recipe-recipe-pop) - [Clear All Modules in a Recipe: `recipe reset`](#clear-all-modules-in-a-recipe-recipe-reset) - [Delete a Recipe: `recipe delete`](#delete-a-recipe-recipe-delete) @@ -168,7 +168,7 @@ Format: `select module {moduleArgument}`, `select recipe :information_source: `recipeName` is the name of an available recipe that is loaded by the program. -> :exclamation: Currently, the `moduleArgument` is needed **only** when using the `rotencode` module, the rest of the currently available modules do not need the `moduleArgument`. +> :information_source: You may refer [here](#selectModuleExamples) for more information how which modules require `moduleArgument`. Examples: @@ -222,7 +222,7 @@ Example: Show all the modules in a recipe, in the order they were added. -Format: `recipe list {recipeName>}` +Format: `recipe list {recipeName}` > :information_source: When `recipeName` is not specified, modules from the recipe that is currently being edited will be listed instead. @@ -295,12 +295,12 @@ Coming soon to a cinema near you in the future*…* |---------------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------| | Enter input data | `input ` | `input HelloWorld!` | | List available modules/recipe | `list {category}` | `list` `list module`
`list recipe` | -| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | +| Select processing module/recipe
| `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | | Reset to original data | `reset` | `reset` | | Create new recipe | `recipe new ` | `recipe new testRecipe` | | Select recipe for editing | `recipe select ` | `recipe select testRecipe` | | List modules in recipe | `recipe list {recipeName}` | `recipe list testRecipe` | -| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | +| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | | Remove module from recipe | `recipe pop` | `recipe pop` | | Clear all modules in recipe | `recipe reset` | `recipe reset` | | Delete recipe | `recipe delete ` | `recipe delete testRecipe` | From 6b4a7bbbac354896b49ca1ac01211b0bc2dbaf8a Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 18:56:19 +0800 Subject: [PATCH 430/607] Additional handling for the listing of files. --- src/main/java/decodex/storage/Storage.java | 9 ++++++--- src/main/java/decodex/ui/messages/ErrorMessages.java | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index f62d11ce61..a97c83ec89 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -98,11 +98,15 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager * * @return The list of recipe File objects. */ - private File[] getAllRecipeFiles() { + private File[] getAllRecipeFiles() throws StorageException { File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); File[] files = recipeDirectory.listFiles(); + if (files == null) { + throw new StorageException(ErrorMessages.FAILED_TO_LIST_FILES_MESSAGE); + } + File[] recipeFiles = Arrays.stream(files) .filter(file -> file.isFile()) .filter(file -> { @@ -280,8 +284,7 @@ private void instantiateDirectoryIfNotExist(String directoryName) throws IOExcep throw new StorageException(formattedErrorMessage); } - boolean isSuccessful; - isSuccessful = directory.mkdir(); + boolean isSuccessful = directory.mkdir(); if (!isSuccessful) { throw new IOException(ErrorMessages.DIRECTORY_INSTANTIATION_FAILED_MESSAGE + directoryName); } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 85259a994b..46d22b3bbb 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -36,4 +36,5 @@ public class ErrorMessages { public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; public static final String INVALID_DIRECTORY_ACCESS = "Failed to access %s directory"; public static final String INVALID_RECIPE_FILE = "The recipe file is not a valid file"; + public static final String FAILED_TO_LIST_FILES_MESSAGE = "Something went wrong when listing files in recipe"; } From 9cb465218cd75308037a3ba2d934307a5870f38d Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 18:57:16 +0800 Subject: [PATCH 431/607] Changed checking to instead be using endswith function. --- src/main/java/decodex/storage/Storage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index a97c83ec89..38d7ab7aac 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -111,7 +111,7 @@ private File[] getAllRecipeFiles() throws StorageException { .filter(file -> file.isFile()) .filter(file -> { String fileName = file.getName(); - return fileName.contains(RECIPE_FILE_PREFIX); + return fileName.endsWith(RECIPE_FILE_PREFIX); }) .toArray(size -> new File[size]); From 196f0864c6b84975b513bf11313a41b697b41d2a Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:58:38 +0800 Subject: [PATCH 432/607] Update recipe deselect description --- docs/UserGuide.md | 34 +++++++++---------- .../recipe/RecipeDeselectCommand.java | 2 +- .../java/decodex/recipes/RecipeManager.java | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 49f8273ab3..c279833c25 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -221,13 +221,13 @@ Example: #### Deselect the Current Editing Recipe: `recipe deselect` -Deselects the current editing recipe. +Deselects the recipe currently being edited. Format: `recipe deselect` Example: -- `recipe deselect` Deselects the current editing recipe. +- `recipe deselect` Deselects the recipe currently being edited. ![recipeDeselect.png](images/recipeDeselect.png) @@ -304,21 +304,21 @@ Coming soon to a cinema near you in the future*…* ## Command Summary -| Action | Command Format | Example Usage | -| ------------------------------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| Enter input data | `input ` | `input HelloWorld!` | -| List available modules/recipe | `list {category}` | `list` `list module`
`list recipe` | -| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | -| Reset to original data | `reset` | `reset` | -| Create new recipe | `recipe new ` | `recipe new testRecipe` | -| Select recipe for editing | `recipe select ` | `recipe select testRecipe` | -| Deselects the current editing recipe | `recipe deselect` | `recipe deselect` | -| List modules in recipe | `recipe list ` | `recipe list testRecipe` | -| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | -| Remove module from recipe | `recipe pop` | `recipe pop` | -| Clear all modules in recipe | `recipe reset` | `recipe reset` | -| Delete recipe | `recipe delete ` | `recipe delete testRecipe` | -| Exit program | `exit` | `exit` | +| Action | Command Format | Example Usage | +| ------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| Enter input data | `input ` | `input HelloWorld!` | +| List available modules/recipe | `list {category}` | `list` `list module`
`list recipe` | +| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | +| Reset to original data | `reset` | `reset` | +| Create new recipe | `recipe new ` | `recipe new testRecipe` | +| Select recipe for editing | `recipe select ` | `recipe select testRecipe` | +| Deselects the recipe currently being edited | `recipe deselect` | `recipe deselect` | +| List modules in recipe | `recipe list ` | `recipe list testRecipe` | +| Add module to recipe | `recipe push {moduleArgument}` | `recipe push base64encode`
`recipe push rotencode 13` | +| Remove module from recipe | `recipe pop` | `recipe pop` | +| Clear all modules in recipe | `recipe reset` | `recipe reset` | +| Delete recipe | `recipe delete ` | `recipe delete testRecipe` | +| Exit program | `exit` | `exit` | ## FAQ diff --git a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java index 072e2a5f2a..5ddafe0e0b 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java @@ -18,7 +18,7 @@ public class RecipeDeselectCommand extends Command { public static final String COMMAND_WORD = "deselect"; - public static final String COMMAND_HELP_MESSAGE = "Deselect editing recipe\n" + public static final String COMMAND_HELP_MESSAGE = "Deselects the recipe currently being edited\n" + "Syntax: recipe deselect"; public RecipeDeselectCommand() { diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index b15db83ed4..7c879aa0fa 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -124,7 +124,7 @@ public void selectRecipeForEditing(String name) throws RecipeManagerException { } /** - * Deselects the current editing recipe. + * Deselects the recipe currently being edited. */ public void deselectEditingRecipe() { logger.fine(String.format("[RecipeManager] Deselected recipe %s", editingRecipeName)); From 4577d4c20e803fd438f901250b7bcccdf43e3f41 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 19:07:25 +0800 Subject: [PATCH 433/607] Update text-ui-test expected --- text-ui-test/EXPECTED.TXT | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 6a7750e043..37fb7a5b05 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -36,6 +36,9 @@ Here are the recipe commands: -> Select recipe for editing Syntax: recipe select +-> Deselects the recipe currently being edited + Syntax: recipe deselect + -> Add module to recipe Syntax: recipe push {moduleArgument} From e501dabc437532d788490be9a1811afeb81ab09b Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Mon, 1 Nov 2021 19:13:53 +0800 Subject: [PATCH 434/607] Changes to module list section Changes to replace the list of modules to table format --- docs/UserGuide.md | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 43f7ced141..56459cf765 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -84,26 +84,20 @@ This section serves to help you better understand the terminologies used in this 6. For more information on Decodex's features, please refer to the Features Section. ## List of Available Modules - -> :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. - -1. Base64 - 1. `base64encode` - Encodes the data using base64 format - 2. `base64decode` - Decodes the data using base64 format -2. Hexadecimal - 1. `hexencode` - Converts the input string to hexadecimal bytes - 2. `hexdecode` - Converts a hexadecimal string back into its raw value -3. Binary - 1. `binencode` - Encodes the data using binary format - 2. `bindecode` - Decodes the data using binary format -4. Rotational Cipher - 1. `rotencode` - Rotates alphabetical characters by a specified integer offset +Below, you can see the list of available modules, what they do and their respective usage formats. | Module | Description | `moduleArgument` | | -------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `base64encode` | Encodes the data using base64 format. | N.A | +| `base64decode` | Decodes the data using base64 format. | N.A | +| `hexencode` | Converts the input string to hexadecimal bytes. | N.A | +| `hexdecode` | Converts a hexadecimal string back into its raw value. | N.A | +| `binencode` | Encodes the data using binary format. | N.A | +| `bindecode` | Decodes the data using binary format. | N.A | | `rotencode` | Rotates alphabetical characters by a specified integer offset.

E.g., `select module rotencode 13` | The rotation offset.

Must be an integer between -2147483648 (MIN_INT) and 2147483647 (MAX_INT). | +> :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. + ## Features > :information_source: Arguments enclosed in `<>` are **mandatory** arguments while arguments enclosed in `{}` are **optional** arguments. For example, `select {moduleArgument}` would mean that `moduleName` is mandatory while `moduleArgument` is optional. @@ -168,7 +162,7 @@ Format: `select module {moduleArgument}`, `select recipe :information_source: `recipeName` is the name of an available recipe that is loaded by the program. -> :information_source: You may refer [here](#selectModuleExamples) for more information how which modules require `moduleArgument`. +> :information_source: You may also refer [here](#selectModuleExamples) for more details on our modules and its usage. Examples: @@ -295,7 +289,7 @@ Coming soon to a cinema near you in the future*…* |---------------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------| | Enter input data | `input ` | `input HelloWorld!` | | List available modules/recipe | `list {category}` | `list` `list module`
`list recipe` | -| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | +| Select processing module/recipe | `select module `
 OR
`select recipe ` | `select module base64decode`
`select module rotencode 13` `select recipe testRecipe1` | | Reset to original data | `reset` | `reset` | | Create new recipe | `recipe new ` | `recipe new testRecipe` | | Select recipe for editing | `recipe select ` | `recipe select testRecipe` | From 4abe298dbc60979cdf4219fb84aadc35f76f7414 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Mon, 1 Nov 2021 19:54:23 +0800 Subject: [PATCH 435/607] Changes to instead print error failed to laod files into one line. --- src/main/java/decodex/storage/Storage.java | 9 ++++----- src/main/java/decodex/ui/Ui.java | 14 ++++++++------ .../java/decodex/ui/messages/ErrorMessages.java | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index 38d7ab7aac..ad2f17a054 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -73,7 +73,7 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager if (recipeFiles.length == EMPTY_LENGTH) { return; } - ArrayList errorsOfFailedRecipeLoads = new ArrayList<>(); + ArrayList failedRecipes = new ArrayList<>(); for (File recipeFile : recipeFiles) { String recipeFilename = recipeFile.getName(); String recipeName = recipeFilename.split(FILENAME_EXTENSION_SPLIT_REGEX)[RECIPE_NAME_INDEX]; @@ -82,15 +82,14 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager recipeManager.addRecipe(recipe); } catch (IOException | ModuleException | ModuleManagerException | RecipeManagerException | RecipeException err) { - String failedToLoadMessage = recipeName + ErrorMessages.FAILED_TO_LOAD_RECIPE_FILE_MESSAGE; - errorsOfFailedRecipeLoads.add(failedToLoadMessage); + failedRecipes.add(recipeName); } } - if (errorsOfFailedRecipeLoads.isEmpty()) { + if (failedRecipes.isEmpty()) { return; } - ui.printFailedToLoadFromStorageMessage(errorsOfFailedRecipeLoads); + ui.printFailedToLoadFromStorageMessage(failedRecipes); } /** diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index e3b35872e5..b3987c749e 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -5,6 +5,7 @@ import decodex.modules.Module; import decodex.recipes.Recipe; +import decodex.ui.messages.ErrorMessages; import decodex.ui.messages.RegularMessages; /** @@ -15,6 +16,7 @@ public class Ui { private static final String PROMPT_HEADER = "Decodex [%s] > "; private static final String PROMPT_EDITING = "Editing: "; private static final String BLANK_STRING = ""; + private static final String COMMA_SEPARATOR = ", "; private static final String SUCCESS_ICON = "[+]"; private static final String ERROR_ICON = "[x]"; private static final String INPUT_PREFIX = "Input:"; @@ -226,14 +228,14 @@ public void printNewRecipeCreated(String recipeName) { // @@author Kair0s3 /** - * Prints the list of message for the recipe files that failed to load. + * Prints all the recipe files that failed to load. * - * @param failedFilesMessageList The list of messages for the failed recipe files. + * @param failedRecipeFiles The list of messages for the failed recipe files. */ - public void printFailedToLoadFromStorageMessage(ArrayList failedFilesMessageList) { - for (String failedFileMessage : failedFilesMessageList) { - System.out.println(ERROR_ICON + " " + failedFileMessage); - } + public void printFailedToLoadFromStorageMessage(ArrayList failedRecipeFiles) { + String failedRecipeFilesString = String.join(COMMA_SEPARATOR, failedRecipeFiles); + String errorMessage = ErrorMessages.FAILED_TO_LOAD_RECIPES_FILE_MESSAGE + " " + failedRecipeFilesString; + System.out.println(ERROR_ICON + " " + errorMessage); } // @@author diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index 1dbcf4778b..a048e4ef30 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -33,7 +33,7 @@ public class ErrorMessages { public static final String FILE_READ_ERROR_MESSAGE = "Something went wrong when reading from the file"; public static final String DIRECTORY_INSTANTIATION_FAILED_MESSAGE = "Failed to create the directory for "; public static final String RECIPE_FILE_DELETE_FAILED_MESSAGE = "Failed to delete the corresponding recipe file"; - public static final String FAILED_TO_LOAD_RECIPE_FILE_MESSAGE = "failed to be loaded into Decodex"; + public static final String FAILED_TO_LOAD_RECIPES_FILE_MESSAGE = "Failed to load following recipes into Decodex:"; public static final String INVALID_RECIPE_COMMAND = "Invalid recipe command"; public static final String INVALID_DIRECTORY_ACCESS = "Failed to access %s directory"; public static final String INVALID_RECIPE_FILE = "The recipe file is not a valid file"; From 516e04a19566d5d784a618b96f55020e15af596a Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Mon, 1 Nov 2021 22:05:28 +0800 Subject: [PATCH 436/607] Break complex line down --- .../java/decodex/commands/recipe/RecipeDeselectCommand.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java index 5ddafe0e0b..247795f94a 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java @@ -11,6 +11,7 @@ import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; @@ -29,7 +30,8 @@ public RecipeDeselectCommand() { public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, Storage storage) throws CommandException, ModuleManagerException, DataManagerException, ModuleException, RecipeException, RecipeManagerException, IOException { - String recipeName = recipeManager.getEditingRecipe().getName(); + Recipe editingRecipe = recipeManager.getEditingRecipe(); + String recipeName = editingRecipe.getName(); recipeManager.deselectEditingRecipe(); ui.printRecipeDeselected(recipeName); } From 33428d952660b402cc6e9cb82446aa33076fe5e9 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 2 Nov 2021 17:05:54 +0800 Subject: [PATCH 437/607] Add recipeName clarification --- docs/UserGuide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7a20e1240d..ce5e568506 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -195,6 +195,8 @@ Creates a new recipe and sets it as currently being edited. Format: `recipe new ` +> :information_source: `recipeName` can only contain alphabets, numbers and underscores. + Example: - `recipe new testRecipe` Creates a new recipe with the name `testRecipe`. From 9ece2006c94c1912e78cf3343dbd0f5d7b6513e2 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 2 Nov 2021 17:57:47 +0800 Subject: [PATCH 438/607] Add explanation on separators in data --- docs/UserGuide.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index ce5e568506..f6fbbc27f5 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -85,19 +85,20 @@ This section serves to help you better understand the terminologies used in this 6. For more information on Decodex's features, please refer to the Features Section. ## List of Available Modules -Below, you can see the list of available modules, what they do and their respective usage formats. +Below, you can see the list of available modules and what they do. + +> :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. + | Module | Description | `moduleArgument` | | -------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `base64encode` | Encodes the data using base64 format. | N.A | -| `base64decode` | Decodes the data using base64 format. | N.A | +| `base64decode` | Decodes the data using base64 format. | N.A | | `hexencode` | Converts the input string to hexadecimal bytes. | N.A | -| `hexdecode` | Converts a hexadecimal string back into its raw value. | N.A | +| `hexdecode` | Converts a hexadecimal string back into its raw value.

Note that this module does not accept any form of separators (space, commas, etc.) in the data. | N.A | | `binencode` | Encodes the data using binary format. | N.A | -| `bindecode` | Decodes the data using binary format. | N.A | -| `rotencode` | Rotates alphabetical characters by a specified integer offset.

E.g., `select module rotencode 13` | The rotation offset.

Must be an integer between -2147483648 (MIN_INT) and 2147483647 (MAX_INT). | - -> :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. +| `bindecode` | Decodes the data using binary format.

Note that this module does not accept any form of separators (space, commas, etc.) in the data. | N.A | +| `rotencode` | Rotates alphabetical characters by a specified integer offset.

E.g., `select module rotencode 13`, `recipe push rotencode 13` | The rotation offset.

Must be an integer between `-2,147,483,648` (`MIN_INT`) and `2,147,483,647` (`MAX_INT`). | ## Features From 3a037a1e9fc6856333db44f3dcc2e6e961309d95 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Tue, 2 Nov 2021 18:21:59 +0800 Subject: [PATCH 439/607] Fix command argument issues --- src/main/java/decodex/parser/Parser.java | 16 ++++++++++++++-- .../java/decodex/parser/RecipeCommandParser.java | 13 +++++++------ .../java/decodex/ui/messages/ErrorMessages.java | 1 + 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index f6baa2d828..cd130dc45e 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -54,6 +54,11 @@ public class Parser { private static final int SELECT_ITEM_NAME_INDEX = 2; private static final int SELECT_PARAMETERS_START_INDEX = 3; + /** + * Specifies the number of tokens for exit command. + */ + private static final int EXIT_COMMAND_LENGTH = 1; + private static final String RECIPE_COMMAND_WORD = "recipe"; private static final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); @@ -116,7 +121,7 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc switch (commandType) { case ExitCommand.COMMAND_WORD: - command = prepareExitCommand(); + command = prepareExitCommand(userInput); break; case InputCommand.COMMAND_WORD: command = prepareInputCommand(userInput); @@ -159,9 +164,16 @@ protected String[] getTokens(String userInput) { /** * Prepares and returns the ExitCommand. * + * @param userInput The user input specified by the user. * @return The ExitCommand object. + * @throws CommandException If the command has any arguments. */ - private ExitCommand prepareExitCommand() { + private ExitCommand prepareExitCommand(String userInput) throws CommandException { + String[] tokens = getTokens(userInput); + + if (tokens.length != EXIT_COMMAND_LENGTH) { + throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); + } return new ExitCommand(); } diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index a2d6a31d73..50481c061b 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -1,18 +1,17 @@ package decodex.parser; -import java.util.Arrays; - import decodex.commands.Command; import decodex.commands.recipe.RecipeDeleteCommand; import decodex.commands.recipe.RecipeDeselectCommand; +import decodex.commands.recipe.RecipeListCommand; import decodex.commands.recipe.RecipeNewCommand; import decodex.commands.recipe.RecipePopCommand; import decodex.commands.recipe.RecipePushCommand; import decodex.commands.recipe.RecipeResetCommand; import decodex.commands.recipe.RecipeSelectCommand; -import decodex.commands.recipe.RecipeListCommand; import decodex.data.exception.CommandException; import decodex.ui.messages.ErrorMessages; +import java.util.Arrays; /** * The RecipeCommandParser class handles the parsing and validation of recipe commands. @@ -84,14 +83,16 @@ public Command parseCommand(String userInput) throws CommandException { /** * Checks the number of arguments is valid. * - * @param subCommandTokens The list of recipe subcommand tokens. - * @param numberOfArguments The valid number of arguments. + * @param subCommandTokens The list of recipe subcommand tokens. + * @param numberOfArguments The valid number of arguments. * @throws CommandException If the number of arguments is invalid. */ private void checkValidNumberOfArguments(String[] subCommandTokens, int numberOfArguments) throws CommandException { - if (subCommandTokens.length != numberOfArguments) { + if (subCommandTokens.length > numberOfArguments) { throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); + } else if (subCommandTokens.length < numberOfArguments) { + throw new CommandException(ErrorMessages.TOO_FEW_COMMAND_ARGUMENTS); } } diff --git a/src/main/java/decodex/ui/messages/ErrorMessages.java b/src/main/java/decodex/ui/messages/ErrorMessages.java index a048e4ef30..c0d10459b9 100644 --- a/src/main/java/decodex/ui/messages/ErrorMessages.java +++ b/src/main/java/decodex/ui/messages/ErrorMessages.java @@ -20,6 +20,7 @@ public class ErrorMessages { public static final String RECIPE_NOT_FOUND_MESSAGE = "The given recipe could not be found."; public static final String EDITING_RECIPE_NOT_FOUND_MESSAGE = "No recipe selected for editing."; public static final String TOO_MANY_COMMAND_ARGUMENTS = "Too many command arguments"; + public static final String TOO_FEW_COMMAND_ARGUMENTS = "Too few command arguments"; public static final String INVALID_LIST_CATEGORY = "Invalid list category"; public static final String UNKNOWN_MODULE_NAME = "Unknown module name"; public static final String INVALID_ROTENCODE_PARAMETER = "The rotation offset must be an integer between " From 1cca6ac82187d20a26079e4629fc7c5bd4ea665a Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 2 Nov 2021 21:59:30 +0800 Subject: [PATCH 440/607] Update module description to follow UG --- src/main/java/decodex/modules/binary/BinaryDecoder.java | 2 +- src/main/java/decodex/modules/binary/BinaryEncoder.java | 2 +- src/main/java/decodex/modules/hex/HexDecoder.java | 2 +- src/main/java/decodex/modules/hex/HexEncoder.java | 2 +- src/main/java/decodex/modules/rot/RotEncoder.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java index ee61890c48..6e194ac00b 100644 --- a/src/main/java/decodex/modules/binary/BinaryDecoder.java +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -13,7 +13,7 @@ public class BinaryDecoder extends Module { public static final String MODULE_NAME = "bindecode"; - public static final String MODULE_DESCRIPTION = "Decodes the data from binary."; + public static final String MODULE_DESCRIPTION = "Decodes the data using binary format."; private static final int BINARY_RADIX = 2; private static final String VALID_BINARY_REGEX = "^([01]{8})*$"; diff --git a/src/main/java/decodex/modules/binary/BinaryEncoder.java b/src/main/java/decodex/modules/binary/BinaryEncoder.java index 97c113be1e..13a0e4a7d7 100644 --- a/src/main/java/decodex/modules/binary/BinaryEncoder.java +++ b/src/main/java/decodex/modules/binary/BinaryEncoder.java @@ -9,7 +9,7 @@ public class BinaryEncoder extends Module { public static final String MODULE_NAME = "binencode"; - public static final String MODULE_DESCRIPTION = "Encodes the data into binary."; + public static final String MODULE_DESCRIPTION = "Encodes the data using binary format."; private static final int BYTE_LENGTH = 8; private static final String BINARY_ZERO_PREFIX = "0"; diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 8ceeae2ad2..1b3e6e7041 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -13,7 +13,7 @@ public class HexDecoder extends Module { public static final String MODULE_NAME = "hexdecode"; - public static final String MODULE_DESCRIPTION = "Converts a hexadecimal string back into its raw value"; + public static final String MODULE_DESCRIPTION = "Converts a hexadecimal string back into its raw value."; public static final int HEXADECIMAL_RADIX = 16; public static final String REGEX_SPLIT_EVERY_2_CHARS = "(?<=\\G..)"; diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index fddd4e9ca4..fea219aba8 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -9,7 +9,7 @@ public class HexEncoder extends Module { public static final String MODULE_NAME = "hexencode"; - public static final String MODULE_DESCRIPTION = "Converts the input string to hexadecimal bytes"; + public static final String MODULE_DESCRIPTION = "Converts the input string to hexadecimal bytes."; public HexEncoder() { super(MODULE_NAME, MODULE_DESCRIPTION); diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java index 7c99f8680f..51b3bacb7a 100644 --- a/src/main/java/decodex/modules/rot/RotEncoder.java +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -9,7 +9,7 @@ public class RotEncoder extends Module { public static final String MODULE_NAME = "rotencode"; - public static final String MODULE_DESCRIPTION = "Rotates the alphabets in the input string by a specified amount"; + public static final String MODULE_DESCRIPTION = "Rotates alphabetical characters by a specified integer offset."; private static final char LOWERCASE_A = 'a'; private static final char UPPERCASE_A = 'A'; From 0e95877214ba7333e191c6a2e46720bed6393c34 Mon Sep 17 00:00:00 2001 From: rizemon Date: Tue, 2 Nov 2021 22:04:31 +0800 Subject: [PATCH 441/607] Update module list message in text-ui --- text-ui-test/EXPECTED.TXT | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 37fb7a5b05..30c343fb18 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -63,11 +63,11 @@ Decodex [] > [+] Reverted all changes Decodex [] > Here is the list of supported modules: base64encode - Encodes the data using Base64 format. base64decode - Decodes the data using Base64 format. - hexencode - Converts the input string to hexadecimal bytes - hexdecode - Converts a hexadecimal string back into its raw value - binencode - Encodes the data into binary. - bindecode - Decodes the data from binary. - rotencode - Rotates the alphabets in the input string by a specified amount + hexencode - Converts the input string to hexadecimal bytes. + hexdecode - Converts a hexadecimal string back into its raw value. + binencode - Encodes the data using binary format. + bindecode - Decodes the data using binary format. + rotencode - Rotates alphabetical characters by a specified integer offset. You do not have any recipes From e635a8dacaffef70a3f91b49d0484671ed2b86ef Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 13:52:14 +0800 Subject: [PATCH 442/607] Abstracted some parts of the decodex logic and simplfied the while loop logic, to instead run until, a exit command is found, then break. --- src/main/java/decodex/Decodex.java | 33 ++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 4126085aeb..778fe06c3e 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -16,6 +16,7 @@ import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; + import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -77,9 +78,7 @@ public void run() { } catch (RecipeManagerException e) { editingRecipeName = null; } - ui.printPromptHeader(editingRecipeName); - String userInput = ui.readInput(); - logger.fine("User input: " + userInput); + String userInput = promptAndGetUserInput(editingRecipeName); try { command = parser.parseCommand(userInput); assert command != null : "Command should not be null"; @@ -89,9 +88,35 @@ public void run() { ui.printError(err); logger.fine(err.getMessage()); } - } while (!(command instanceof ExitCommand)); + if (command instanceof ExitCommand) { + break; + } + } while (true); } + + /** + * Prompts and returns the user input. + * + * @param editingRecipeName The name of the currently editing recipe. + * @return The user input. + */ + private String promptAndGetUserInput(String editingRecipeName) { + ui.printPromptHeader(editingRecipeName); + String userInput = ui.readInput(); + logger.fine("User input: " + userInput); + return userInput; + } + + /** + * Loads the saved recipes stored in the recipe directory. + * + * @throws IOException If an error occurred when creating the recipe directory, + * or reading the recipe files. + * @throws StorageException If the recipe directory is not an actual directory, + * or if it failed to list the recipe files, + * or if the respective recipe file is not an actual file. + */ private void loadSavedRecipes() throws IOException, StorageException { storage.loadRecipesFromDirectory(moduleManager, recipeManager, ui); } From 23efcf442495640fd4b308b04d4ef404d2d3bf59 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 13:52:40 +0800 Subject: [PATCH 443/607] Changes to class member's Javadocs to be single line instead. --- src/main/java/decodex/parser/Parser.java | 27 ++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index f6baa2d828..cde7868e8c 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -19,42 +19,33 @@ */ public class Parser { - /** - * Specifies the index of the tokens where specific parameters can be found. - */ + // Specifies the index of the tokens where specific parameters can be found. private static final int COMMAND_INDEX = 0; - /** - * Specifies the starting index where the arguments can be found. - */ + // Specifies the starting index where the arguments can be found. protected static final int STARTING_ARGUMENTS_INDEX = 1; - /** - * Specifies the token used to split the user input by. - */ + // Specifies the token used to split the user input by. private static final String SPLIT_REGEX = "\\s+"; - /** - * Specifies the valid length of the tokens and used to check validity of tokens. - */ + // Specifies the valid length of the tokens and used to check validity of tokens. private static final int VALID_TOKENS_LENGTH_FOR_COMMAND = 1; - /** - * Specifies the number of tokens for list command with no category and with a category. - */ + // Specifies the number of tokens for list command with no category and with a category. private static final int LIST_COMMAND_LENGTH_NO_CATEGORY = 1; private static final int LIST_COMMAND_LENGTH_HAS_CATEGORY = 2; private static final int LIST_COMMAND_CATEGORY_STARTING_INDEX = 1; - /** - * Specifies the valid number of tokens and indexes for category and parameters for select command. - */ + // Specifies the valid number of tokens and indexes for category and parameters for select command. private static final int VALID_TOKENS_LENGTH_FOR_SELECT_COMMAND = 3; private static final int SELECT_CATEGORY_INDEX = 1; private static final int SELECT_ITEM_NAME_INDEX = 2; private static final int SELECT_PARAMETERS_START_INDEX = 3; + // Specifies the keyword for recipe related commands. private static final String RECIPE_COMMAND_WORD = "recipe"; + + // Initializes the RecipeCommandParser for parsing recipe commands. private static final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); /** From 3b54d6bd7b762a56a3b35ff7f819ba115e8e3b61 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 16:49:10 +0800 Subject: [PATCH 444/607] Refactoring to replace constant javadocs to be single line instead. And replace one of the EMPTY STRING magic literals. --- src/main/java/decodex/storage/Storage.java | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index ad2f17a054..b3e5b48c84 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -24,32 +24,27 @@ // @@author Kair0s3 public class Storage { - /** - * Specifies the default directories for input, output and recipes respectively. - */ + // Specifies the default directories for input, output and recipes respectively. private static final String DEFAULT_RECIPE_DIRECTORY = "recipe"; - /** - * Specifies the corresponding file prefixes. - */ + // Specifies the corresponding file prefixes. private static final String RECIPE_FILE_PREFIX = ".txt"; private static final String LINE_BREAK_REGEX = "\\r?\\n"; private static final String FILENAME_EXTENSION_SPLIT_REGEX = "[.]"; - /** - * Specifies the index for the corresponding fields. - */ + // Specifies the index for the corresponding fields. private static final int RECIPE_NAME_INDEX = 0; private static final int MODULE_NAME_INDEX = 0; private static final int STARTING_PARAMETER_INDEX = 1; - /** - * Other miscellaneous constants for condition checking. - */ + // Specifies other miscellaneous constants for condition checking. private static final int EMPTY_LENGTH = 0; + // Specifies constant values. + private static final String EMPTY_STRING = ""; + /** * Initializes a new Storage. */ @@ -62,8 +57,11 @@ public Storage() { * @param moduleManager The ModuleManager object. * @param recipeManager The RecipeManager object. * @param ui The Ui object. - * @throws IOException If an error occurred when reading the file. - * @throws StorageException If the recipe directory is not a file. + * @throws IOException If an error occurred when creating the recipe directory, + * or reading the recipe files. + * @throws StorageException If the recipe directory is not an actual directory, + * or if it failed to list the recipe files, + * or if the respective recipe file is not an actual file. */ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager recipeManager, Ui ui) throws IOException, StorageException { @@ -96,6 +94,7 @@ public void loadRecipesFromDirectory(ModuleManager moduleManager, RecipeManager * Gets all the recipe files in the recipe directory. * * @return The list of recipe File objects. + * @throws StorageException If it failed to list the recipe files. */ private File[] getAllRecipeFiles() throws StorageException { File recipeDirectory = new File(DEFAULT_RECIPE_DIRECTORY); @@ -237,10 +236,10 @@ private void saveRecipeModulesToFile(File writeFile, Recipe recipeToBeSaved) thr */ private String formatModuleListForSaving(ArrayList modules) { if (modules.isEmpty()) { - return ""; + return EMPTY_STRING; } - String formattedModuleList = ""; + String formattedModuleList = EMPTY_STRING; for (Module module : modules) { formattedModuleList = formattedModuleList.concat(module.toString() + "\n"); } @@ -270,7 +269,8 @@ public void deleteRecipeFile(String recipeName) throws IOException, StorageExcep /** * Instantiates the given directory if it does not exist yet. * - * @throws IOException If the directory is a file. + * @throws IOException If the directory failed to be created. + * @throws StorageException If the given directory is not an actual directory. */ private void instantiateDirectoryIfNotExist(String directoryName) throws IOException, StorageException { File directory = new File(directoryName); From 57b4fb547df2d19c963446648f3c3a50e929237f Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 16:57:16 +0800 Subject: [PATCH 445/607] Replace multiline variable javadocs to single line. --- src/main/java/decodex/Decodex.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 778fe06c3e..24e3ecade4 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -25,10 +25,7 @@ public class Decodex { public static Logger logger = Logger.getLogger(Decodex.class.getName()); - - /** - * Necessary objects to be initialized for Decodex to work properly. - */ + // Necessary objects to be initialized for Decodex to work properly. private static DataManager dataManager; private static ModuleManager moduleManager; private static RecipeManager recipeManager; @@ -66,6 +63,9 @@ public static void main(String[] args) { new Decodex().run(); } + /** + * Runs the main logic of Decodex. + */ public void run() { ui.printGreeting(); From c526e30e79ac73e1be9c47a154d3051b6a33f117 Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Wed, 3 Nov 2021 18:15:35 +0800 Subject: [PATCH 446/607] PPP Draft 1 My current draft for PPP (only halfway/quarterly done) --- docs/team/kair0s3.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/team/kair0s3.md diff --git a/docs/team/kair0s3.md b/docs/team/kair0s3.md new file mode 100644 index 0000000000..5c9ee1ca1e --- /dev/null +++ b/docs/team/kair0s3.md @@ -0,0 +1,35 @@ +# Project Portfolio Page - Decodex + +## Overview + +This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). + +I am Sim Tian Boon, one of the contributors in this `Decodex` project. + +## Project Name: `Decodex` +`Decodex` is a Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly process data from one encoding format to another with extreme ease in the form of intuitive shorthand usage. + +## Summary of Contributions +Below are my contributions to the `Decodex` + +Features/Functionalities + +1. Added ability to parse user inputs (For non-recipe commands) + - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. + - Justification - Helps to parse the user input so that `Decodex` runs the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands are passed to `RecipeCommandParser`. + - PR - [222](3333) + +2. Added the ability to show current data + - Feature - Shows the user the current data that they have. + - eg. before base64encode, if the input is "hi", then `show` will show the user the current data that is "hi". But after base64encode is done, then the current data is "aGk=" + - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). + - PR: + +3. Added the Base64 Modules + - Feature - Allows users to use the module to encode/decode using base64 formats. + - Justification - This is one of the more common encoding formats that appear in CTFs. + - PR: + +4. Added ability to load recipes on start up + - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. + - Justification From 9dccb64fa5d137d6cb9ba2259b8add0ce30b300e Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Wed, 3 Nov 2021 18:17:20 +0800 Subject: [PATCH 447/607] Fixing of indents --- docs/team/kair0s3.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/team/kair0s3.md b/docs/team/kair0s3.md index 5c9ee1ca1e..514e759ec9 100644 --- a/docs/team/kair0s3.md +++ b/docs/team/kair0s3.md @@ -15,21 +15,21 @@ Below are my contributions to the `Decodex` Features/Functionalities 1. Added ability to parse user inputs (For non-recipe commands) - - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. - - Justification - Helps to parse the user input so that `Decodex` runs the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands are passed to `RecipeCommandParser`. - - PR - [222](3333) + - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. + - Justification - Helps to parse the user input so that `Decodex` runs the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands are passed to `RecipeCommandParser`. + - PR - [222](3333) 2. Added the ability to show current data - - Feature - Shows the user the current data that they have. - - eg. before base64encode, if the input is "hi", then `show` will show the user the current data that is "hi". But after base64encode is done, then the current data is "aGk=" - - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). - - PR: + - Feature - Shows the user the current data that they have. + - eg. before base64encode, if the input is "hi", then `show` will show the user the current data that is "hi". But after base64encode is done, then the current data is "aGk=" + - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). + - PR: 3. Added the Base64 Modules - - Feature - Allows users to use the module to encode/decode using base64 formats. - - Justification - This is one of the more common encoding formats that appear in CTFs. - - PR: + - Feature - Allows users to use the module to encode/decode using base64 formats. + - Justification - This is one of the more common encoding formats that appear in CTFs. + - PR: 4. Added ability to load recipes on start up - - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. - - Justification + - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. + - Justification From 4c1caa75d087c9eb6117391539a2c2c87f68b60c Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 3 Nov 2021 19:43:01 +0800 Subject: [PATCH 448/607] Add PPP --- docs/team/rizemon.md | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/team/rizemon.md diff --git a/docs/team/rizemon.md b/docs/team/rizemon.md new file mode 100644 index 0000000000..a77c86cc70 --- /dev/null +++ b/docs/team/rizemon.md @@ -0,0 +1,52 @@ +# Project Portfolio Page - Decodex + +## Overview + +This is a project in accordance to the fulfilment of the Software Engineering & Object-Oriented Programming (`CS2113T`) module. + +I am Tan Jia Le, one of the contributors for `Decodex`. + +## Project Name: `Decodex` +`Decodex` is a Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to perform rapid encoding, decoding, encryption, decryption of data with ease and without any programming needed. + +## Summary of Contributions +Below are my contributions to the `Decodex` + +### Features/Functionalities + +1. Added the `input` command (Formally known as the `data` command): + 1. What it does: Allow the user to enter the data that they wish to execute the modules/recipes on. + 2. Related pull requests: [#52](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/52), [#112](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/112) + 3. Underlying components that were implemented along with it: `Data` class ([#32](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/32)), `DataManager` class ([#34](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/34), [#46](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/46)) + +2. Added the `reset` command: + 1. What it does: Allow the user to revert any changes done to the data that they first inputted. + 2. Related pull requests: [#65](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/65) + +3. Added the `RotEncoder` (Rotational Cipher) module: + 1. What it does: Allow the user to perform the rotational cipher on the current data. + 2. Related pull requests: [#148](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/148) + +4. Added the `recipe select`, `recipe push`, `recipe pop`, `recipe reset` commands: + 1. What they do: Allow the user to select a recipe and perform modifications to it. + 2. Related pull requests: [#190](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/190), [#192](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/192), [#194](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/194), [#196](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/196) + +5. Added the `help` command: + 1. What it does: Allow the user to view the syntax and purpose of the commands. + 2. Related pull requests: [#226](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/226) + +Code contributed: more than 2000 lines of code ([Reposense](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=rizemon&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=rizemon&tabRepo=AY2122S1-CS2113T-T10-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false)) + +### Enhancements to Existing Features + +1. Wrote tests for `RecipeManager` class: [#171](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/171) +2. Decoupled `Storage` class from the `RecipeManager` class: [#250](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/250) +3. Added naming restrictions to recipes using Regex: [#206](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/206) + +### Review/mentoring contributions +1. Reviewed teammates' PRs: [69 out of 132 PRs](https://github.com/AY2122S1-CS2113T-T10-3/tp/pulls?q=is%3Apr+is%3Aclosed+-author%3A%40me+commenter%3A%40me+reviewed-by%3A%40me) (Also ranked #2 on [tP comments dashboard](https://nus-cs2113-ay2122s1.github.io/dashboards/contents/tp-comments.html)) +2. Reported bugs/comments for other teams: ([1](https://github.com/rizemon/ped/issues/5), [2](https://github.com/rizemon/ped/issues/12), [3](https://github.com/rizemon/ped/issues/13), [4](https://github.com/rizemon/ped/issues/7), [5](https://github.com/rizemon/ped/issues/1)) + +### User Guide + +### Developer Guide From cc1acafc12c64560af276d6e14836e588a464507 Mon Sep 17 00:00:00 2001 From: Kair0s3 <50107219+Kair0s3@users.noreply.github.com> Date: Wed, 3 Nov 2021 22:05:45 +0800 Subject: [PATCH 449/607] Updated PPP (Left with 25%) Also, I need to finish up my PR to fix the code and update accordingly. --- docs/team/kair0s3.md | 47 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/docs/team/kair0s3.md b/docs/team/kair0s3.md index 514e759ec9..6a16beac73 100644 --- a/docs/team/kair0s3.md +++ b/docs/team/kair0s3.md @@ -4,7 +4,7 @@ This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). -I am Sim Tian Boon, one of the contributors in this `Decodex` project. +I am **Sim Tian Boon**, one of the contributors of `Decodex`. ## Project Name: `Decodex` `Decodex` is a Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly process data from one encoding format to another with extreme ease in the form of intuitive shorthand usage. @@ -12,24 +12,51 @@ I am Sim Tian Boon, one of the contributors in this `Decodex` project. ## Summary of Contributions Below are my contributions to the `Decodex` +*Do note that the PR links shows just the more notable ones, since the other PRs may also consist of changes/adjustments to this functionality/features* + Features/Functionalities -1. Added ability to parse user inputs (For non-recipe commands) +1. Added `Parser` functionality to parse user inputs (For non-recipe commands) - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. - - Justification - Helps to parse the user input so that `Decodex` runs the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands are passed to `RecipeCommandParser`. - - PR - [222](3333) + - Justification - Helps to parse the user input so that `Decodex` can run the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands will have it's additional parsing done on `RecipeCommandParser`. + - PR - [#53](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/53), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/files) 2. Added the ability to show current data - Feature - Shows the user the current data that they have. - eg. before base64encode, if the input is "hi", then `show` will show the user the current data that is "hi". But after base64encode is done, then the current data is "aGk=" - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). - - PR: + - PR - [#274](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/274) -3. Added the Base64 Modules - - Feature - Allows users to use the module to encode/decode using base64 formats. +3. Added Base64 Modules (Base64 Encoder and Decoder) + - Feature - Allows users to use these module to encode/decode using base64 formats. - Justification - This is one of the more common encoding formats that appear in CTFs. - - PR: + - PR - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35) -4. Added ability to load recipes on start up +4. Added ability to load recipes from files on start up - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. - - Justification + - Justification - Allows users to load previously saved recipes, instead of having to create from scratch each time. + - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) + +5. Added ability to save recipe into file, and delete the recipe file + - Functionality - Saves the recipe into a file in the format of `recipeName.txt` and stored in the `recipe/` directory. Do note that, this is more of an auto-save whenever changes are made to `recipe`. For the deletion, the recipe file is deleted when the user deletes the `recipe` from `Decodex`. + - Justification - Allows users to auto-save their crafted recipes into a file which provides convenience for users. And the deletion of the recipe file also provides convenience over having to delete manually. + - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) + + +## Enhancements to Existing Features + - Refactored and improved Coding Standards + - `Storage` - Remove the magic literals [#tobeupdated with the new commit](rrr) and removed dead code [#251](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/251/files) + - `Decodex` - Grouped a code fragment together and made the while loop less complex/more readable [#tobeupdated with the new commit](rrr) + - Assertion for `Parser` - [#99](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/99/files) + - Logging for `Decodex.class` - [#96](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/96) + - Wrote JUnit Test for Base64 Modules and Parser - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/), [#91](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/91) + - Attempted to standardize coding styles/documentation - [#47](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/47), [#147](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/147) + - I learnt that this is probably one of the hardest thing to do since everyone has their own style of codes and the way they describe/explain things. + - JavaDocs for `Decodex` and `Storage` - Some notable ones ([#tobeupdated with the new commit](rrr)) + +## Documentation + - [User Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/UserGuide.html) + - [Developer Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/DeveloperGuide.html) + + +## Community From c6a76d1272e7d7a0844dd5e41c4d5703740845bf Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 3 Nov 2021 22:13:15 +0800 Subject: [PATCH 450/607] Update AboutUs links to PPP --- docs/AboutUs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index dad9cf36f4..679dd3a21c 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,7 +2,7 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: - | Tan Jia Le | [Github](https://github.com/rizemon) | [Portfolio](docs/team/johndoe.md) - | Aloysius Lee | [Github](https://github.com/arraysius) | [Portfolio](docs/team/johndoe.md) - | Sean Phang | [Github](https://github.com/SeenFang) | [Portfolio](docs/team/johndoe.md) - | Sim Tian Boon | [Github](https://github.com/Kair0s3) | [Portfolio](docs/team/johndoe.md) + | Tan Jia Le | [Github](https://github.com/rizemon) | [Portfolio](team/rizemon.md) + | Aloysius Lee | [Github](https://github.com/arraysius) | [Portfolio](team/arraysius.md) + | Sean Phang | [Github](https://github.com/SeenFang) | [Portfolio](team/seenfang.md) + | Sim Tian Boon | [Github](https://github.com/Kair0s3) | [Portfolio](team/kair0s3.md) From 48afc8be363d2e61b1bbed364377638359299829 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 22:58:53 +0800 Subject: [PATCH 451/607] Updated JavaDocs and additional line of javadoc for variable. --- src/main/java/decodex/storage/Storage.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index b3e5b48c84..fd7930f094 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -30,6 +30,7 @@ public class Storage { // Specifies the corresponding file prefixes. private static final String RECIPE_FILE_PREFIX = ".txt"; + // Specifies the different regexes. private static final String LINE_BREAK_REGEX = "\\r?\\n"; private static final String FILENAME_EXTENSION_SPLIT_REGEX = "[.]"; @@ -190,8 +191,10 @@ private byte[] readContentFromFile(Path readFilePath) throws IOException, FileNo * Saves the provided recipe into a file. * * @param recipe The recipe to be saved. - * @throws IOException If an error occurred when creating the file - * or when writing to the file. + * @throws IOException If an error occurred when creating the file + * or when writing to the file. + * @throws StorageException If the recipe directory is not an actual directory + * or if the recipe file is not an actual file. */ public void saveRecipeToFile(Recipe recipe) throws IOException, StorageException { instantiateDirectoryIfNotExist(DEFAULT_RECIPE_DIRECTORY); @@ -208,8 +211,9 @@ public void saveRecipeToFile(Recipe recipe) throws IOException, StorageException * * @param writeFile The file to be saved to. * @param recipeToBeSaved The recipe to be saved. - * @throws IOException If an error occurred when creating the file - * or when writing to the file. + * @throws IOException If an error occurred when creating the file + * or when writing to the file. + * @throws StorageException If the recipe file is not an actual file. */ private void saveRecipeModulesToFile(File writeFile, Recipe recipeToBeSaved) throws IOException, StorageException { // To ensure only writes to file happen. @@ -250,6 +254,9 @@ private String formatModuleListForSaving(ArrayList modules) { * Deletes the saved recipe file. * * @param recipeName The name of the deleted recipe. + * @throws IOException If an error occurred when deleting the file + * or when the file failed to be deleted. + * @throws StorageException If the recipe file is not an actual file. */ public void deleteRecipeFile(String recipeName) throws IOException, StorageException { String newRecipeFileName = recipeName + RECIPE_FILE_PREFIX; From 90e416e490e90f0e9683ac163f3826b94c0ee3f3 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 23:12:21 +0800 Subject: [PATCH 452/607] Added author tags for base64 decoder and encoder. --- src/main/java/decodex/modules/base64/Base64Decoder.java | 1 + src/main/java/decodex/modules/base64/Base64Encoder.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index dfdad60c55..16d2e97b95 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -6,6 +6,7 @@ import decodex.ui.messages.ErrorMessages; import java.util.Base64; +// @@author Kair0s3 /** * Base64Encoder serves to carry out the Base64 decoding operations. */ diff --git a/src/main/java/decodex/modules/base64/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java index eea1250c6e..eaff65a6d5 100644 --- a/src/main/java/decodex/modules/base64/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -5,6 +5,7 @@ import java.util.Base64; +// @@author Kair0s3 /** * Base64Encoder serves to carry out the Base64 encoding operations. */ From 9f388712e17ce76ab3690f7901a74f40550aab38 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 3 Nov 2021 23:46:16 +0800 Subject: [PATCH 453/607] Refactor RotEncoder * Convert comments for class members to single line --- .../java/decodex/modules/rot/RotEncoder.java | 38 +++++++++++++------ .../java/decodex/recipes/RecipeManager.java | 8 +--- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/main/java/decodex/modules/rot/RotEncoder.java b/src/main/java/decodex/modules/rot/RotEncoder.java index 51b3bacb7a..23050ab72d 100644 --- a/src/main/java/decodex/modules/rot/RotEncoder.java +++ b/src/main/java/decodex/modules/rot/RotEncoder.java @@ -7,10 +7,11 @@ * The RotEncoder class handles the rotation of the alphabets in the input string. */ public class RotEncoder extends Module { - + public static final String MODULE_NAME = "rotencode"; public static final String MODULE_DESCRIPTION = "Rotates alphabetical characters by a specified integer offset."; + // Constants for performing rotational cipher private static final char LOWERCASE_A = 'a'; private static final char UPPERCASE_A = 'A'; private static final int ALPHABETS_COUNT = 26; @@ -23,7 +24,7 @@ public RotEncoder(int rotateOffset) { } /** - * Applies the rotation to the given Data object. + * Applies the rotational cipher to the given Data object. * * @param data The given Data object. * @return A Data object with its alphabetical characters rotated. @@ -33,19 +34,32 @@ public Data run(Data data) { StringBuilder encodedStringBuilder = new StringBuilder(); for (byte b : data.getRawBytes()) { - char updatedChar = (char) b; - if (Character.isLowerCase((char) b)) { - updatedChar = rotateLowercaseAlphabet((char) b, rotateOffset); - } else if (Character.isUpperCase((char) b)) { - updatedChar = rotateUppercaseAlphabet((char) b, rotateOffset); - } - encodedStringBuilder.append(updatedChar); + char letter = (char) b; + char updatedLetter = rotateAlphabet(letter, rotateOffset); + encodedStringBuilder.append(updatedLetter); } String encodedString = encodedStringBuilder.toString(); return new Data(encodedString); } + /** + * Rotates the given alphabet. + * + * @param c The given alphabet. + * @param rotateOffset Number of positions to rotate the alphabet by. + * @return The updated alphabet. + */ + private char rotateAlphabet(char c, int rotateOffset) { + char updatedChar = c; + if (Character.isLowerCase(c)) { + updatedChar = rotateLowercaseAlphabet(c, rotateOffset); + } else if (Character.isUpperCase(c)) { + updatedChar = rotateUppercaseAlphabet(c, rotateOffset); + } + return updatedChar; + } + /** * Rotates the given lowercase alphabet. * @@ -55,7 +69,8 @@ public Data run(Data data) { */ private char rotateLowercaseAlphabet(char c, int rotateOffset) { int originalAlphabetIndex = c - LOWERCASE_A; - int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + rotateOffset, ALPHABETS_COUNT); + int roundedRotateOffset = rotateOffset % ALPHABETS_COUNT; + int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + roundedRotateOffset, ALPHABETS_COUNT); return (char) (LOWERCASE_A + updatedAlphabetIndex); } @@ -68,7 +83,8 @@ private char rotateLowercaseAlphabet(char c, int rotateOffset) { */ private char rotateUppercaseAlphabet(char c, int rotateOffset) { int originalAlphabetIndex = c - UPPERCASE_A; - int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + rotateOffset, ALPHABETS_COUNT); + int roundedRotateOffset = rotateOffset % ALPHABETS_COUNT; + int updatedAlphabetIndex = Math.floorMod(originalAlphabetIndex + roundedRotateOffset, ALPHABETS_COUNT); return (char) (UPPERCASE_A + updatedAlphabetIndex); } diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 7c879aa0fa..659a01d0e1 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -20,14 +20,10 @@ public class RecipeManager { private static int RECIPE_NAME_STRING_INDEX = 0; - /** - * Stores mapping between recipe name and recipe. - */ + // Stores mapping between recipe name and recipe. private HashMap recipeList; - /** - * Name of the recipe that is selected for editing/modification. - */ + // Name of the recipe that is selected for editing/modification. private String editingRecipeName; /** From 05e4c57538c10425c67b428f64e156db3f46cc32 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 23:49:31 +0800 Subject: [PATCH 454/607] Some additional refactoring and simplifying some of the codes in `Parser` --- src/main/java/decodex/parser/Parser.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 76a35b9f06..161de131c2 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -25,8 +25,12 @@ public class Parser { // Specifies the starting index where the arguments can be found. protected static final int STARTING_ARGUMENTS_INDEX = 1; - // Specifies the token used to split the user input by. + // Specifies the tokens used to split the user input by. private static final String SPLIT_REGEX = "\\s+"; + private static final int SPLIT_LIMIT_VALUE = -1; + + // Specifies the separator for joining a split array. + private static final String SPACE_SEPARATOR = " "; // Specifies the valid length of the tokens and used to check validity of tokens. private static final int VALID_TOKENS_LENGTH_FOR_COMMAND = 1; @@ -64,7 +68,7 @@ public String getCommandType(String userInput) throws ParserException { throw new ParserException(ErrorMessages.MISSING_COMMAND_TYPE); } - String[] tokens = strippedUserInput.split(" "); + String[] tokens = strippedUserInput.split(SPLIT_REGEX); boolean isInvalidTokensLength = tokens.length < VALID_TOKENS_LENGTH_FOR_COMMAND; @@ -86,10 +90,10 @@ public String getCommandType(String userInput) throws ParserException { */ public String getInputString(String userInput) throws ParserException { String strippedUserInput = userInput.stripLeading(); - String[] tokens = strippedUserInput.split(" ", -1); + String[] tokens = strippedUserInput.split(SPLIT_REGEX, SPLIT_LIMIT_VALUE); - String argumentString = String.join(" ", - Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length)); + String[] argumentArray = Arrays.copyOfRange(tokens, STARTING_ARGUMENTS_INDEX, tokens.length); + String argumentString = String.join(SPACE_SEPARATOR, argumentArray); if (argumentString.isEmpty()) { throw new ParserException(ErrorMessages.INPUT_EMPTY); } From 91a4bf0ae1f57924d3ea60c7e7f7ed2408a029eb Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 23:52:22 +0800 Subject: [PATCH 455/607] Reverted the change for the while to a simpler fix as per suggestion --- src/main/java/decodex/Decodex.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 24e3ecade4..84a3b903a5 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -70,7 +70,7 @@ public void run() { ui.printGreeting(); Command command = null; - + boolean isExit; do { String editingRecipeName; try { @@ -88,10 +88,8 @@ public void run() { ui.printError(err); logger.fine(err.getMessage()); } - if (command instanceof ExitCommand) { - break; - } - } while (true); + isExit = command instanceof ExitCommand; + } while (!isExit); } From cf88a4928a7e39e01446ff1d85aa66759ddf64c7 Mon Sep 17 00:00:00 2001 From: rizemon Date: Wed, 3 Nov 2021 23:53:12 +0800 Subject: [PATCH 456/607] Remove dead getDescription of Module.java * Compress method signatures of commands to less lines --- src/main/java/decodex/commands/InputCommand.java | 3 +-- src/main/java/decodex/commands/recipe/RecipePopCommand.java | 3 +-- src/main/java/decodex/commands/recipe/RecipePushCommand.java | 4 ++-- src/main/java/decodex/commands/recipe/RecipeResetCommand.java | 3 +-- .../java/decodex/commands/recipe/RecipeSelectCommand.java | 3 +-- src/main/java/decodex/modules/Module.java | 4 ---- 6 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index 27f515ded6..71eb18e21b 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -25,8 +25,7 @@ public InputCommand(String dataString) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) - throws CommandException { + Storage storage) throws CommandException { if (dataString.isEmpty()) { throw new CommandException(ErrorMessages.MISSING_ARGUMENT); } diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java index 1e93db96c1..c33cd126f0 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -26,8 +26,7 @@ public RecipePopCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) - throws RecipeException, RecipeManagerException, IOException { + Storage storage) throws RecipeException, RecipeManagerException, IOException { Module module = recipeManager.popModuleFromEditedRecipe(); Recipe editingRecipe = recipeManager.getEditingRecipe(); try { diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java index 93ca864b87..47eb829166 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -34,8 +34,8 @@ public RecipePushCommand(String moduleName, String[] parameters) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) - throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException, IOException { + Storage storage) throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException, + IOException { if (moduleName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); } diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java index c6081926fd..dc0bfa5814 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -24,8 +24,7 @@ public RecipeResetCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) - throws RecipeManagerException, IOException { + Storage storage) throws RecipeManagerException, IOException { Recipe editingRecipe = recipeManager.getEditingRecipe(); recipeManager.resetEditedRecipe(); try { diff --git a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java index c6f7a18077..abda38f9b9 100644 --- a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java @@ -26,8 +26,7 @@ public RecipeSelectCommand(String recipeName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) - throws CommandException, RecipeManagerException { + Storage storage) throws CommandException, RecipeManagerException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } diff --git a/src/main/java/decodex/modules/Module.java b/src/main/java/decodex/modules/Module.java index b9d9576ce3..bc7f7e69a1 100644 --- a/src/main/java/decodex/modules/Module.java +++ b/src/main/java/decodex/modules/Module.java @@ -20,10 +20,6 @@ public String getName() { return name; } - public String getDescription() { - return description; - } - /** * Returns a String formatted Module. * From cc3ed147acf9e994f85119218e7b5b707352a7f0 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Wed, 3 Nov 2021 23:55:39 +0800 Subject: [PATCH 457/607] Made suggested changes #2 --- src/main/java/decodex/Decodex.java | 18 +++--------------- src/main/java/decodex/parser/Parser.java | 2 +- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 84a3b903a5..07f5a1242d 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -78,7 +78,9 @@ public void run() { } catch (RecipeManagerException e) { editingRecipeName = null; } - String userInput = promptAndGetUserInput(editingRecipeName); + ui.printPromptHeader(editingRecipeName); + String userInput = ui.readInput(); + logger.fine("User input: " + userInput); try { command = parser.parseCommand(userInput); assert command != null : "Command should not be null"; @@ -92,20 +94,6 @@ public void run() { } while (!isExit); } - - /** - * Prompts and returns the user input. - * - * @param editingRecipeName The name of the currently editing recipe. - * @return The user input. - */ - private String promptAndGetUserInput(String editingRecipeName) { - ui.printPromptHeader(editingRecipeName); - String userInput = ui.readInput(); - logger.fine("User input: " + userInput); - return userInput; - } - /** * Loads the saved recipes stored in the recipe directory. * diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 161de131c2..7e35c1655b 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -53,7 +53,7 @@ public class Parser { private static final int EXIT_COMMAND_LENGTH = 1; // Initializes the RecipeCommandParser for parsing recipe commands. - private static final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); + private final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); /** * Returns the type of command that the user has specified. From 2424d459820d0cbc08ce9bacccfb79223c56af13 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 00:12:10 +0800 Subject: [PATCH 458/607] Reverted changes due to realization of a stack overflow error, since parent creates child then child calls back to parent's constructor and it goes in an infinite loop --- src/main/java/decodex/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 7e35c1655b..161de131c2 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -53,7 +53,7 @@ public class Parser { private static final int EXIT_COMMAND_LENGTH = 1; // Initializes the RecipeCommandParser for parsing recipe commands. - private final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); + private static final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); /** * Returns the type of command that the user has specified. From 4553dca256a37d1018d9bda31d4f6eb5c5027534 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 00:27:38 +0800 Subject: [PATCH 459/607] Some changes to the phrasing and most importantly NO SPOILERS!! Pretty sure you can see the spoiler already Hey! I think you must be bored, maybe go watch some movies instead! Anyways have a nice day! P.S It is night time now, AND why are u still reading this... RED LIGHT GREEN LIGHT `GUN SHOTS` https://tinyurl.com/4r8ck3uf --- src/main/java/decodex/parser/Parser.java | 2 +- src/main/java/decodex/storage/Storage.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 161de131c2..b1a4b0af18 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -29,7 +29,7 @@ public class Parser { private static final String SPLIT_REGEX = "\\s+"; private static final int SPLIT_LIMIT_VALUE = -1; - // Specifies the separator for joining a split array. + // Specifies the separator used to reconstruct a string from a list of tokens. private static final String SPACE_SEPARATOR = " "; // Specifies the valid length of the tokens and used to check validity of tokens. diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index fd7930f094..a3dc0b8762 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -24,7 +24,7 @@ // @@author Kair0s3 public class Storage { - // Specifies the default directories for input, output and recipes respectively. + // Specifies the default directories for recipes. private static final String DEFAULT_RECIPE_DIRECTORY = "recipe"; // Specifies the corresponding file prefixes. @@ -43,7 +43,7 @@ public class Storage { // Specifies other miscellaneous constants for condition checking. private static final int EMPTY_LENGTH = 0; - // Specifies constant values. + // Specifies the constant values. private static final String EMPTY_STRING = ""; /** From 57d10d1c6229912e0c1f96c04926c2a483e1c817 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 00:48:01 +0800 Subject: [PATCH 460/607] Last change of the day for compressing the method signature (the throw exceptions part to standardized one as of the PR #282) --- src/main/java/decodex/commands/ShowCommand.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index 389b990bdb..d047647d1c 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -20,16 +20,16 @@ public ShowCommand() { /** * Shows the current data. * - * @param dataManager The DataManager object. + * @param dataManager The DataManager object. * @param moduleManager The ModuleManager object. - * @param ui The Ui object. + * @param ui The Ui object. * @param recipeManager The RecipeManager object. - * @param storage The Storage object. + * @param storage The Storage object. * @throws DataManagerException If the current data is empty. */ @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws DataManagerException { + Storage storage) throws DataManagerException { Data currentData = dataManager.getCurrentData(); String currentDataInput = currentData.toString(); ui.printCurrentData(currentDataInput); From 13ea71f2b251232a0d145deee4d8324da28ff1d7 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 00:52:31 +0800 Subject: [PATCH 461/607] Removed the additional indent. --- src/main/java/decodex/commands/ShowCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index d047647d1c..d6a7bd4a27 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -29,7 +29,7 @@ public ShowCommand() { */ @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws DataManagerException { + Storage storage) throws DataManagerException { Data currentData = dataManager.getCurrentData(); String currentDataInput = currentData.toString(); ui.printCurrentData(currentDataInput); From 5b831e50d5dca07730271c6f49563c8f8342cfde Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Thu, 4 Nov 2021 16:17:31 +0800 Subject: [PATCH 462/607] Simplify HexDecode --- src/main/java/decodex/modules/hex/HexDecoder.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index 1b3e6e7041..ee2adef22a 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -4,8 +4,6 @@ import decodex.data.exception.ModuleException; import decodex.modules.Module; import decodex.ui.messages.ErrorMessages; -import java.util.Arrays; -import java.util.stream.Collectors; /** * The HexDecoder class converts a hexadecimal string back into its raw value. @@ -37,10 +35,15 @@ public Data run(Data data) throws ModuleException { throw new ModuleException(ErrorMessages.HEX_DECODING_FAILED_MESSAGE); } - String decodedString = Arrays - .stream(inputString.split(REGEX_SPLIT_EVERY_2_CHARS)) - .map(s -> Character.toString((char) Integer.parseInt(s, HEXADECIMAL_RADIX))) - .collect(Collectors.joining()); + StringBuilder decodedStringBuilder = new StringBuilder(); + String[] hexSplit = inputString.split(REGEX_SPLIT_EVERY_2_CHARS); + + for (String hexChar : hexSplit){ + char parsedChar = (char)Integer.parseInt(hexChar, HEXADECIMAL_RADIX); + decodedStringBuilder.append(parsedChar); + } + + String decodedString = decodedStringBuilder.toString(); return new Data(decodedString); } From c503c1d173872aaf25d41664275d452561b8af6e Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:20:24 +0800 Subject: [PATCH 463/607] Add author tags --- .../java/decodex/commands/ListCommand.java | 1 + .../decodex/modules/binary/BinaryDecoder.java | 1 + .../decodex/modules/binary/BinaryEncoder.java | 1 + .../java/decodex/recipes/RecipeManager.java | 20 +++++++++++-------- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index 69b62d636e..b625b8eb68 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -8,6 +8,7 @@ import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; +// @@author arraysius public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java index 6e194ac00b..906a7a7e5a 100644 --- a/src/main/java/decodex/modules/binary/BinaryDecoder.java +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -7,6 +7,7 @@ import decodex.data.exception.ModuleException; import decodex.ui.messages.ErrorMessages; +// @@author arraysius /** * The BinaryDecoder class handles the binary decoding operations. */ diff --git a/src/main/java/decodex/modules/binary/BinaryEncoder.java b/src/main/java/decodex/modules/binary/BinaryEncoder.java index 13a0e4a7d7..bdf6719e0a 100644 --- a/src/main/java/decodex/modules/binary/BinaryEncoder.java +++ b/src/main/java/decodex/modules/binary/BinaryEncoder.java @@ -3,6 +3,7 @@ import decodex.data.Data; import decodex.modules.Module; +// @@author arraysius /** * The BinaryEncoder class handles the binary encoding operations. */ diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 7c879aa0fa..cbc0c4a662 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -38,6 +38,7 @@ public RecipeManager() { editingRecipeName = null; } + // @@author arraysius /** * Returns a sorted list of recipe names. * @@ -49,6 +50,7 @@ public String[] getRecipeNames() { Arrays.sort(recipeNameArray); return recipeNameArray; } + // @@author /** * Adds a recipe to recipe manager to be managed. @@ -123,14 +125,6 @@ public void selectRecipeForEditing(String name) throws RecipeManagerException { logger.fine(String.format("[RecipeManager] Selected recipe %s", name)); } - /** - * Deselects the recipe currently being edited. - */ - public void deselectEditingRecipe() { - logger.fine(String.format("[RecipeManager] Deselected recipe %s", editingRecipeName)); - editingRecipeName = null; - } - /** * Appends a new module to the end of the recipe currently being edited. * @@ -164,4 +158,14 @@ public void resetEditedRecipe() throws RecipeManagerException { editingRecipe.reset(); } // @@author + + // @@author arraysius + /** + * Deselects the recipe currently being edited. + */ + public void deselectEditingRecipe() { + logger.fine(String.format("[RecipeManager] Deselected recipe %s", editingRecipeName)); + editingRecipeName = null; + } + // @@author } From c006a31459801919e1fb5c325d2ae9dc6521caad Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:26:22 +0800 Subject: [PATCH 464/607] Add author tags for `Module.java` --- src/main/java/decodex/modules/Module.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/decodex/modules/Module.java b/src/main/java/decodex/modules/Module.java index bc7f7e69a1..6a0ab27cdb 100644 --- a/src/main/java/decodex/modules/Module.java +++ b/src/main/java/decodex/modules/Module.java @@ -3,6 +3,7 @@ import decodex.data.Data; import decodex.data.exception.ModuleException; +// @@author arraysius /** * Module serves as a foundation for other modules to be built upon. */ @@ -19,7 +20,9 @@ public Module(String name, String description) { public String getName() { return name; } + // @@author + // @@author Kair0s3 /** * Returns a String formatted Module. * @@ -29,7 +32,9 @@ public String getName() { public String toString() { return name; } + // @@author + // @@author arraysius /** * Performs data processing on the provided Data object. Abstract method to be implemented by other modules. * @@ -37,3 +42,4 @@ public String toString() { */ public abstract Data run(Data data) throws ModuleException; } +// @@author From 07ab3eef21207eb0dacd7db6855a593f230f517a Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 21:10:09 +0800 Subject: [PATCH 465/607] Some bug fixes to ensure that if any additional parameters given to show and help will be deemed as invalid - too many arguments. as opposed to the bug where, `show` and `help` works even when an additional parameter is given. --- src/main/java/decodex/parser/Parser.java | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index b1a4b0af18..ab7681868a 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -49,8 +49,10 @@ public class Parser { // Specifies the keyword for recipe related commands. private static final String RECIPE_COMMAND_WORD = "recipe"; - // Specifies the number of tokens for exit command. + // Specifies the number of tokens for commands. private static final int EXIT_COMMAND_LENGTH = 1; + private static final int SHOW_COMMAND_LENGTH = 1; + private static final int HELP_COMMAND_LENGTH = 1; // Initializes the RecipeCommandParser for parsing recipe commands. private static final RecipeCommandParser recipeCommandParser = new RecipeCommandParser(); @@ -129,10 +131,10 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc command = prepareSelectCommand(userInput); break; case HelpCommand.COMMAND_WORD: - command = prepareHelpCommand(); + command = prepareHelpCommand(userInput); break; case ShowCommand.COMMAND_WORD: - command = prepareShowCommand(); + command = prepareShowCommand(userInput); break; case RECIPE_COMMAND_WORD: command = prepareRecipeSubcommands(userInput); @@ -250,7 +252,12 @@ private SelectCommand prepareSelectCommand(String userInput) throws CommandExcep * @return The ShowCommand object. * @throws CommandException If the original data input is empty. */ - private ShowCommand prepareShowCommand() { + private ShowCommand prepareShowCommand(String userInput) throws CommandException { + String[] tokens = getTokens(userInput); + + if (tokens.length > SHOW_COMMAND_LENGTH) { + throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); + } return new ShowCommand(); } @@ -270,7 +277,12 @@ private Command prepareRecipeSubcommands(String userInput) throws CommandExcepti * * @return The HelpCommand object. */ - private HelpCommand prepareHelpCommand() { + private HelpCommand prepareHelpCommand(String userInput) throws CommandException { + String[] tokens = getTokens(userInput); + + if (tokens.length > HELP_COMMAND_LENGTH) { + throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); + } return new HelpCommand(); } } From ccec0726d2a0af7168d887be9b5d7a9700aa7936 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Thu, 4 Nov 2021 21:46:46 +0800 Subject: [PATCH 466/607] Add PPP --- docs/team/seenfang.md | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/team/seenfang.md diff --git a/docs/team/seenfang.md b/docs/team/seenfang.md new file mode 100644 index 0000000000..744fb048c1 --- /dev/null +++ b/docs/team/seenfang.md @@ -0,0 +1,47 @@ +# Project Portfolio Page - Decodex + +## Overview + +This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). + +I am **Sean Phang**, one of the contributors of `Decodex`. + +## Project Name: `Decodex` +`Decodex` is a Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly process data from one encoding format to another with extreme ease in the form of intuitive shorthand usage. + +## Summary of Contributions +Below are my contributions to `Decodex` + +- Code contributed: [RepoSense](https://nus-cs2113-ay2122s1.github.io/tp-dashboard/?search=SeenFang&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-25&tabOpen=true&tabType=authorship&tabAuthor=SeenFang&tabRepo=AY2122S1-CS2113T-T10-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false) + +### Features/Functionalities + +1. Added hexadecimal modules (`hexdecode` and `hexencode`) + - Allows users to use these modules to encode/decode hexadecimal formats. + - Related pull requests - [#36](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/36), [#37](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/37) + +2. Added the `recipe` and the `recipeManager` classes + - Implements the base structure for the recipe functionality that `Decodex` offers. + - Related pull requests - [#156](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/156), [#156](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/157) + +3. Added the commands for `recipe new`, `recipe delete` and `recipe list`. + - Allows the user to create and delete recipes as well as list all currently available recipes + - Related pull requests - [#197](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/197), [#198](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/198), [#222](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/222) + +4. Added automatic saving of recipes to a file + - Adds the functionality of saving a created recipe to a file whenever it is created/modified + - Related pull requests - [#239](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/239) + +5. Added the `exit` command + - Allows the user to exit the program. + - Related pull requests - [#39](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/39) + +## Enhancements to Existing Features +- Added JUnit tests to features written - [#81](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/81) +- Added logging to modules selected - [#97](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/97) +- Added assertions to command - [#101](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/101) +- Shifted the running of modules in a recipe from the command to the `recipe` class - [#187](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/187) + +## Documentation +- [User Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/UserGuide.html) +- [Developer Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/DeveloperGuide.html) From d59e7aa553b3610dfc572ab4f314136c9dd07ea7 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Thu, 4 Nov 2021 21:55:32 +0800 Subject: [PATCH 467/607] Fix style --- src/main/java/decodex/modules/hex/HexDecoder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index ee2adef22a..abcc99bfb0 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -38,8 +38,8 @@ public Data run(Data data) throws ModuleException { StringBuilder decodedStringBuilder = new StringBuilder(); String[] hexSplit = inputString.split(REGEX_SPLIT_EVERY_2_CHARS); - for (String hexChar : hexSplit){ - char parsedChar = (char)Integer.parseInt(hexChar, HEXADECIMAL_RADIX); + for (String hexChar : hexSplit) { + char parsedChar = (char) Integer.parseInt(hexChar, HEXADECIMAL_RADIX); decodedStringBuilder.append(parsedChar); } From 343331b3d903a40d4a6f8ec73012f873676a63b6 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Thu, 4 Nov 2021 22:39:26 +0800 Subject: [PATCH 468/607] Add PPP --- docs/team/arraysius.md | 51 ++++++++++++++++++++++++++++++++++++++++++ docs/team/johndoe.md | 6 ----- 2 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 docs/team/arraysius.md delete mode 100644 docs/team/johndoe.md diff --git a/docs/team/arraysius.md b/docs/team/arraysius.md new file mode 100644 index 0000000000..8b621b2415 --- /dev/null +++ b/docs/team/arraysius.md @@ -0,0 +1,51 @@ +# Project Portfolio Page - Decodex + +## Overview +This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). + +I am **Lee Kai Wen, Aloysius**, one of the contributors of `Decodex`. + +## Project Name: `Decodex` +`Decodex` is a Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly process data from one encoding format to another with extreme ease in the form of intuitive shorthand usage. + +## Summary of Contributions +Given below are my notable contributions to the project. + +### New Features / Functionalities +- Implemented the `ModuleManager` class along with tests. + - What it does: Manages all module related activities (e.g. retriving all modules, preparing module for the `select` command). + - Justficiation: Serves as the centralised location to conduct all module management activities. + - Pull request: ([#43](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/43)) +- Added the `select`, `list` and `recipe deselect` commands along with tests. + - What they do: + - The `select` command allows the user to select a module and run it on the user provided data ([#68](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/68)). + - The `list` command allows the user to list all available modules ([#69](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/69)). + - The `recipe deselect` command deselects the recipe currently being edited ([#275](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/275)). +- Implemented the `Ui`, `RegularMessages` and `ErrorMessages` classes along with tests. + - What it does: `Ui` manages all UI related activities (e.g. Prints messages to the user with the appropriate statuses) while `RegularMessages` and `ErrorMessages` servers as a central location for all messages to be printed. + - Justification: Serves as the centralised location to conduct all UI related activities. + - Pull request: ([#154](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/154)) +- Added the ability to process data to and from binary format along with tests. + - What it does: Allows the user to encode data into binary format and decode from binary format. + - Pull request: ([#155](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/155)) +- Implemented the `RecipeCommandParser` class along with tests. + - What it does: Handles further subcommand parsing for all `recipe` commands. + - Justification: Separation of `recipe` command parsing provided clearer organisation of code. + - Pull request: ([#207](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/207)) + +### Enhancements to Existing Features +- Extended the `select` command to: + - Select modules with parameters ([#166](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/166)) + - Select recipes ([#202](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/202)). +- Extended the `list` command to list recipes ([#183](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/183)). + +## Documentation + - [User Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/UserGuide.html) + - Added introduction and FAQ ([#244](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/244)). + - [Developer Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/DeveloperGuide.html) + - Added arhitecture diagram and details ([#181](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/181)). + - Added implementation details of logic, module and basic commands ([#234](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/234)). + +## Community +- Non-trivial reviewed of team members' PRs ([#39](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/39), [#52](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/52), [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208)). +- Reported bugs for other teams ([1](https://github.com/arraysius/ped/issues/4) [2](https://github.com/arraysius/ped/issues/9) [3](https://github.com/arraysius/ped/issues/12) [4](https://github.com/arraysius/ped/issues/13) [5](https://github.com/arraysius/ped/issues/14)). diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md deleted file mode 100644 index ab75b391b8..0000000000 --- a/docs/team/johndoe.md +++ /dev/null @@ -1,6 +0,0 @@ -# John Doe - Project Portfolio Page - -## Overview - - -### Summary of Contributions From e85b55d2f4bf0a0ff0239515c9a45bdc3bf851dc Mon Sep 17 00:00:00 2001 From: Aloysius Lee <16810847+arraysius@users.noreply.github.com> Date: Thu, 4 Nov 2021 22:42:36 +0800 Subject: [PATCH 469/607] Fix minor typos --- docs/team/arraysius.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/team/arraysius.md b/docs/team/arraysius.md index 8b621b2415..7ae36aed7e 100644 --- a/docs/team/arraysius.md +++ b/docs/team/arraysius.md @@ -13,7 +13,7 @@ Given below are my notable contributions to the project. ### New Features / Functionalities - Implemented the `ModuleManager` class along with tests. - - What it does: Manages all module related activities (e.g. retriving all modules, preparing module for the `select` command). + - What it does: Manages all module related activities (e.g. retrieving all modules, preparing module for the `select` command). - Justficiation: Serves as the centralised location to conduct all module management activities. - Pull request: ([#43](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/43)) - Added the `select`, `list` and `recipe deselect` commands along with tests. @@ -48,4 +48,4 @@ Given below are my notable contributions to the project. ## Community - Non-trivial reviewed of team members' PRs ([#39](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/39), [#52](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/52), [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208)). -- Reported bugs for other teams ([1](https://github.com/arraysius/ped/issues/4) [2](https://github.com/arraysius/ped/issues/9) [3](https://github.com/arraysius/ped/issues/12) [4](https://github.com/arraysius/ped/issues/13) [5](https://github.com/arraysius/ped/issues/14)). +- Reported bugs for other teams ([1](https://github.com/arraysius/ped/issues/4), [2](https://github.com/arraysius/ped/issues/9), [3](https://github.com/arraysius/ped/issues/12), [4](https://github.com/arraysius/ped/issues/13), [5](https://github.com/arraysius/ped/issues/14)). From fd3bd7c0a572c2f5a7d0277dbf2cb92cb31d1375 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 23:26:53 +0800 Subject: [PATCH 470/607] Added changes to PPP --- docs/team/kair0s3.md | 65 ++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/docs/team/kair0s3.md b/docs/team/kair0s3.md index 6a16beac73..21281b5a1c 100644 --- a/docs/team/kair0s3.md +++ b/docs/team/kair0s3.md @@ -17,46 +17,57 @@ Below are my contributions to the `Decodex` Features/Functionalities 1. Added `Parser` functionality to parse user inputs (For non-recipe commands) - - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. - - Justification - Helps to parse the user input so that `Decodex` can run the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands will have it's additional parsing done on `RecipeCommandParser`. - - PR - [#53](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/53), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/files) + - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. + - Justification - Helps to parse the user input so that `Decodex` can run the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands will have it's additional parsing done on `RecipeCommandParser`. + - PR - [#53](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/53), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/files) 2. Added the ability to show current data - - Feature - Shows the user the current data that they have. + - Feature - Shows the user the current data that they have. - eg. before base64encode, if the input is "hi", then `show` will show the user the current data that is "hi". But after base64encode is done, then the current data is "aGk=" - - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). - - PR - [#274](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/274) + - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). + - PR - [#274](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/274) 3. Added Base64 Modules (Base64 Encoder and Decoder) - - Feature - Allows users to use these module to encode/decode using base64 formats. - - Justification - This is one of the more common encoding formats that appear in CTFs. - - PR - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35) + - Feature - Allows users to use these module to encode/decode using base64 formats. + - Justification - This is one of the more common encoding formats that appear in CTFs. + - PR - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35) 4. Added ability to load recipes from files on start up - - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. - - Justification - Allows users to load previously saved recipes, instead of having to create from scratch each time. - - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) + - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. + - Justification - Allows users to load previously saved recipes, instead of having to create from scratch each time. + - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) 5. Added ability to save recipe into file, and delete the recipe file - - Functionality - Saves the recipe into a file in the format of `recipeName.txt` and stored in the `recipe/` directory. Do note that, this is more of an auto-save whenever changes are made to `recipe`. For the deletion, the recipe file is deleted when the user deletes the `recipe` from `Decodex`. - - Justification - Allows users to auto-save their crafted recipes into a file which provides convenience for users. And the deletion of the recipe file also provides convenience over having to delete manually. - - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) + - Functionality - Saves the recipe into a file in the format of `recipeName.txt` and stored in the `recipe/` directory. Do note that, this is more of an auto-save whenever changes are made to `recipe`. For the deletion, the recipe file is deleted when the user deletes the `recipe` from `Decodex`. + - Justification - Allows users to auto-save their crafted recipes into a file which provides convenience for users. And the deletion of the recipe file also provides convenience over having to delete manually. + - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) ## Enhancements to Existing Features - - Refactored and improved Coding Standards - - `Storage` - Remove the magic literals [#tobeupdated with the new commit](rrr) and removed dead code [#251](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/251/files) - - `Decodex` - Grouped a code fragment together and made the while loop less complex/more readable [#tobeupdated with the new commit](rrr) - - Assertion for `Parser` - [#99](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/99/files) - - Logging for `Decodex.class` - [#96](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/96) - - Wrote JUnit Test for Base64 Modules and Parser - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/), [#91](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/91) - - Attempted to standardize coding styles/documentation - [#47](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/47), [#147](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/147) - - I learnt that this is probably one of the hardest thing to do since everyone has their own style of codes and the way they describe/explain things. - - JavaDocs for `Decodex` and `Storage` - Some notable ones ([#tobeupdated with the new commit](rrr)) +- Refactored and improved Coding Standards + - `Storage` - Remove the magic literals [#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280) and removed dead code [#251](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/251/files) + - `Decodex` - Grouped a code fragment together and made the while loop less complex/more readable [#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280) +- Assertion for `Parser` - [#99](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/99/files) +- Logging for `Decodex.class` - [#96](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/96) +- Wrote JUnit Test for Base64 Modules and Parser - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/), [#91](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/91) +- Attempted to standardize coding styles/documentation - [#47](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/47), [#147](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/147) + - I learnt that this is probably one of the hardest thing to do since everyone has their own style of codes and the way they describe/explain things. +- JavaDocs for `Decodex` and `Storage` - Some notable ones ([#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280)) +- Bug Fixes - [#274](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/274), [#286](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/286) ## Documentation - - [User Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/UserGuide.html) - - [Developer Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/DeveloperGuide.html) - +- [User Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/UserGuide.html) + - Added introduction - [#103](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/103) + - Added table of contents - [#220](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/220/files) + - Added basic commands documentation - [#246](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/246) + - Added command summary documentation [#246](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/246) +- [Developer Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/DeveloperGuide.html) + - Added Introduction, Purpose, Terminologies, Symbols - [#231](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/231) + - Added "Before writing code" portion - [#180](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/180) + - Added User Profiles, Value Proposition, User Stories - [#231](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/231) ## Community +- Non-trivial PR reviews - [#39](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/39), [#52](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/52), [#148](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/148) , [#156](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/156), [#166](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/166), [#183](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/183), [#190](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/190), [#192](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/192), [#194](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/194), [#196](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/196), [#275](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/275) +- Contributions to CS2113/T forum - [1 (not tP related)](https://github.com/nus-cs2113-AY2122S1/forum/issues/12), [2](https://github.com/nus-cs2113-AY2122S1/forum/issues/101), [3](https://github.com/nus-cs2113-AY2122S1/forum/issues/110) +- Bugs reported and other team reviews - [1](https://github.com/nus-cs2113-AY2122S1/tp/pull/32), [2](https://github.com/AY2122S1-CS2113T-T09-2/tp/issues/219), [3](https://github.com/AY2122S1-CS2113T-T09-2/tp/issues/210) + , [4](https://github.com/AY2122S1-CS2113T-T09-2/tp/issues/212), [5](https://github.com/AY2122S1-CS2113T-T09-2/tp/issues/198), [6](https://github.com/AY2122S1-CS2113T-T09-2/tp/issues/195), [7](https://github.com/AY2122S1-CS2113T-T09-2/tp/issues/179), [8](https://github.com/AY2122S1-CS2113T-T09-2/tp/issues/184) From 563047615ee7fe3354dc7359baf54d634002d615 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 23:31:25 +0800 Subject: [PATCH 471/607] Removed an additional line --- docs/team/kair0s3.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/team/kair0s3.md b/docs/team/kair0s3.md index 21281b5a1c..d562596af9 100644 --- a/docs/team/kair0s3.md +++ b/docs/team/kair0s3.md @@ -1,10 +1,7 @@ # Project Portfolio Page - Decodex ## Overview - -This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). - -I am **Sim Tian Boon**, one of the contributors of `Decodex`. +This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). And, hi! I am **Sim Tian Boon**, one of the contributors of `Decodex`. ## Project Name: `Decodex` `Decodex` is a Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly process data from one encoding format to another with extreme ease in the form of intuitive shorthand usage. @@ -41,8 +38,7 @@ Features/Functionalities - Functionality - Saves the recipe into a file in the format of `recipeName.txt` and stored in the `recipe/` directory. Do note that, this is more of an auto-save whenever changes are made to `recipe`. For the deletion, the recipe file is deleted when the user deletes the `recipe` from `Decodex`. - Justification - Allows users to auto-save their crafted recipes into a file which provides convenience for users. And the deletion of the recipe file also provides convenience over having to delete manually. - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) - - + ## Enhancements to Existing Features - Refactored and improved Coding Standards - `Storage` - Remove the magic literals [#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280) and removed dead code [#251](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/251/files) From c6f891baaae9ae2e4fa8b72ff1dfbfddaeb746b7 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Thu, 4 Nov 2021 23:37:48 +0800 Subject: [PATCH 472/607] Changes to reduce lines for printing --- docs/team/kair0s3.md | 61 ++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/docs/team/kair0s3.md b/docs/team/kair0s3.md index d562596af9..1d9e8af9d0 100644 --- a/docs/team/kair0s3.md +++ b/docs/team/kair0s3.md @@ -1,7 +1,7 @@ # Project Portfolio Page - Decodex ## Overview -This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). And, hi! I am **Sim Tian Boon**, one of the contributors of `Decodex`. +This is a student project for Software Engineering & Object-Oriented Programming (CS2113/T). And, I am **Sim Tian Boon**, one of the contributors of `Decodex`. ## Project Name: `Decodex` `Decodex` is a Command Line Interface (CLI) application for Capture-The-Flag (CTF) players to quickly process data from one encoding format to another with extreme ease in the form of intuitive shorthand usage. @@ -13,54 +13,49 @@ Below are my contributions to the `Decodex` Features/Functionalities -1. Added `Parser` functionality to parse user inputs (For non-recipe commands) - - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. - - Justification - Helps to parse the user input so that `Decodex` can run the correct command that users expect However, this is only limited to parsing of "Basic" Commands, and `recipe` related commands will have it's additional parsing done on `RecipeCommandParser`. - - PR - [#53](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/53), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/files) +1. Added `Parser` functionality to parse user inputs (For non-recipe commands) ([#53](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/53), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/files)) + - Functionality - Performs the parsing of user input to generate the corresponding command to be executed. + - Justification - Helps to parse the user input so that `Decodex` can run the correct command that users expect Only limited to basic `Decodex` commands. -2. Added the ability to show current data - - Feature - Shows the user the current data that they have. - - eg. before base64encode, if the input is "hi", then `show` will show the user the current data that is "hi". But after base64encode is done, then the current data is "aGk=" - - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). - - PR - [#274](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/274) +2. Added the ability to show current data ([#274](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/274)) + - Feature - Shows the user the current data that they have. + - eg. before base64encode, if the input is "hi", then `show` will show the user the current data that is "hi". But after base64encode is done, then the current data is "aGk=" + - Justification - Allows user to keep track of the current data they are working on, mainly for convenience and also credits to @alwinangys for the [suggestion](https://github.com/alwinangys/ped/issues/4). -3. Added Base64 Modules (Base64 Encoder and Decoder) - - Feature - Allows users to use these module to encode/decode using base64 formats. - - Justification - This is one of the more common encoding formats that appear in CTFs. - - PR - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35) +3. Added Base64 Modules (Base64 Encoder and Decoder) ([#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35)) + - Feature - Allows users to use these module to encode/decode using base64 formats. + - Justification - This is one of the more common encoding formats that appear in CTFs. -4. Added ability to load recipes from files on start up - - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. - - Justification - Allows users to load previously saved recipes, instead of having to create from scratch each time. - - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) +4. Added ability to load recipes from files on start up ([#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/)) + - Functionality - Performs the loading of recipe files from the recipe directory upon run of `Decodex`. + - Justification - Allows users to load previously saved recipes, instead of having to create from scratch each time. + +5. Added ability to save recipe into file, and delete the recipe file ([#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/)) + - Functionality - Saves the recipe into a file in the format of `recipeName.txt` and stored in the `recipe/` directory. Do note that, this is more of an auto-save whenever changes are made to `recipe`. For the deletion, the recipe file is deleted when the user deletes the `recipe` from `Decodex`. + - Justification - Allows users to auto-save their crafted recipes into a file which provides convenience for users. And the deletion of the recipe file also provides convenience over having to delete manually. -5. Added ability to save recipe into file, and delete the recipe file - - Functionality - Saves the recipe into a file in the format of `recipeName.txt` and stored in the `recipe/` directory. Do note that, this is more of an auto-save whenever changes are made to `recipe`. For the deletion, the recipe file is deleted when the user deletes the `recipe` from `Decodex`. - - Justification - Allows users to auto-save their crafted recipes into a file which provides convenience for users. And the deletion of the recipe file also provides convenience over having to delete manually. - - PR - [#208](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/208/) - ## Enhancements to Existing Features - Refactored and improved Coding Standards - - `Storage` - Remove the magic literals [#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280) and removed dead code [#251](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/251/files) - - `Decodex` - Grouped a code fragment together and made the while loop less complex/more readable [#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280) + - `Storage` - Remove the magic literals [#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280) and removed dead code [#251](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/251/files) + - `Decodex` - Grouped a code fragment together and made the while loop less complex/more readable [#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280) - Assertion for `Parser` - [#99](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/99/files) - Logging for `Decodex.class` - [#96](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/96) - Wrote JUnit Test for Base64 Modules and Parser - [#35](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/35), [#72](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/72/), [#91](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/91) - Attempted to standardize coding styles/documentation - [#47](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/47), [#147](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/147) - - I learnt that this is probably one of the hardest thing to do since everyone has their own style of codes and the way they describe/explain things. + - I learnt that this is probably one of the hardest thing to do since everyone has their own style of codes and the way they describe/explain things. - JavaDocs for `Decodex` and `Storage` - Some notable ones ([#280](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/280)) - Bug Fixes - [#274](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/274), [#286](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/286) ## Documentation - [User Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/UserGuide.html) - - Added introduction - [#103](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/103) - - Added table of contents - [#220](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/220/files) - - Added basic commands documentation - [#246](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/246) - - Added command summary documentation [#246](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/246) + - Added introduction - [#103](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/103) + - Added table of contents - [#220](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/220/files) + - Added basic commands documentation - [#246](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/246) + - Added command summary documentation [#246](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/246) - [Developer Guide](https://ay2122s1-cs2113t-t10-3.github.io/tp/DeveloperGuide.html) - - Added Introduction, Purpose, Terminologies, Symbols - [#231](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/231) - - Added "Before writing code" portion - [#180](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/180) - - Added User Profiles, Value Proposition, User Stories - [#231](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/231) + - Added Introduction, Purpose, Terminologies, Symbols - [#231](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/231) + - Added "Before writing code" portion - [#180](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/180) + - Added User Profiles, Value Proposition, User Stories - [#231](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/231) ## Community - Non-trivial PR reviews - [#39](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/39), [#52](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/52), [#148](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/148) , [#156](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/156), [#166](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/166), [#183](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/183), [#190](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/190), [#192](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/192), [#194](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/194), [#196](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/196), [#275](https://github.com/AY2122S1-CS2113T-T10-3/tp/pull/275) From 18445873c38497f61e0dcc356fdd1e704a7f215a Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Fri, 5 Nov 2021 15:10:43 +0800 Subject: [PATCH 473/607] Change File Structure --- src/main/java/decodex/Decodex.java | 4 +- .../java/decodex/commands/HelpCommand.java | 8 ++- .../java/decodex/commands/InputCommand.java | 1 + .../java/decodex/commands/ListCommand.java | 1 + .../java/decodex/commands/ResetCommand.java | 1 + .../java/decodex/commands/SelectCommand.java | 1 + .../java/decodex/commands/ShowCommand.java | 1 + .../recipe/RecipeDeselectCommand.java | 2 +- .../commands/recipe/RecipePopCommand.java | 2 +- .../commands/recipe/RecipePushCommand.java | 2 +- .../commands/recipe/RecipeResetCommand.java | 2 +- .../commands/recipe/RecipeSelectCommand.java | 2 +- .../decodex/{commands => logic}/Command.java | 56 +++++++++---------- .../{ => logic}/commands/ExitCommand.java | 3 +- .../commands/recipe/RecipeDeleteCommand.java | 4 +- .../commands/recipe/RecipeListCommand.java | 4 +- .../commands/recipe/RecipeNewCommand.java | 4 +- src/main/java/decodex/parser/Parser.java | 4 +- .../decodex/parser/RecipeCommandParser.java | 8 +-- .../recipe}/RecipeDeleteCommandTest.java | 5 +- .../recipe}/RecipeListCommandTest.java | 4 +- .../recipe}/RecipeNewCommandTest.java | 5 +- src/test/java/decodex/parser/ParserTest.java | 4 +- .../parser/RecipeCommandParserTest.java | 6 +- 24 files changed, 72 insertions(+), 62 deletions(-) rename src/main/java/decodex/{commands => logic}/Command.java (94%) rename src/main/java/decodex/{ => logic}/commands/ExitCommand.java (90%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipeDeleteCommand.java (95%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipeListCommand.java (97%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipeNewCommand.java (95%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipeDeleteCommandTest.java (94%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipeListCommandTest.java (97%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipeNewCommandTest.java (94%) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 07f5a1242d..71f9bb99bf 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -1,7 +1,7 @@ package decodex; -import decodex.commands.Command; -import decodex.commands.ExitCommand; +import decodex.logic.Command; +import decodex.logic.commands.ExitCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/commands/HelpCommand.java index a9b5df10b8..c9b51f08c3 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/commands/HelpCommand.java @@ -1,9 +1,11 @@ package decodex.commands; -import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.logic.Command; +import decodex.logic.commands.ExitCommand; +import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.commands.recipe.RecipeDeselectCommand; -import decodex.commands.recipe.RecipeListCommand; -import decodex.commands.recipe.RecipeNewCommand; +import decodex.logic.commands.recipe.RecipeListCommand; +import decodex.logic.commands.recipe.RecipeNewCommand; import decodex.commands.recipe.RecipePopCommand; import decodex.commands.recipe.RecipePushCommand; import decodex.commands.recipe.RecipeResetCommand; diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/commands/InputCommand.java index 71eb18e21b..c0f1bd192c 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/commands/InputCommand.java @@ -3,6 +3,7 @@ import decodex.data.Data; import decodex.data.DataManager; import decodex.data.exception.CommandException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/commands/ListCommand.java index b625b8eb68..b749b2fc1d 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/commands/ListCommand.java @@ -2,6 +2,7 @@ import decodex.data.DataManager; import decodex.data.exception.CommandException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/commands/ResetCommand.java index bebd33f220..07bfd0280a 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/commands/ResetCommand.java @@ -1,6 +1,7 @@ package decodex.commands; import decodex.data.DataManager; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/commands/SelectCommand.java index 0f6b06218b..2d3cdc67c7 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/commands/SelectCommand.java @@ -8,6 +8,7 @@ import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.logic.Command; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/commands/ShowCommand.java index d6a7bd4a27..447910d788 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/commands/ShowCommand.java @@ -3,6 +3,7 @@ import decodex.data.Data; import decodex.data.DataManager; import decodex.data.exception.DataManagerException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; diff --git a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java index 247795f94a..c3fed73e5e 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java @@ -2,7 +2,7 @@ import java.io.IOException; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java index c33cd126f0..97d9559258 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -1,6 +1,6 @@ package decodex.commands.recipe; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java index 47eb829166..7d6921fc98 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -1,6 +1,6 @@ package decodex.commands.recipe; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java index dc0bfa5814..bfb6273997 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -1,6 +1,6 @@ package decodex.commands.recipe; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; diff --git a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java index abda38f9b9..2ba2bb1e60 100644 --- a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java @@ -1,6 +1,6 @@ package decodex.commands.recipe; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.RecipeManagerException; diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/logic/Command.java similarity index 94% rename from src/main/java/decodex/commands/Command.java rename to src/main/java/decodex/logic/Command.java index 990d08d0f6..34cadf62e6 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/logic/Command.java @@ -1,28 +1,28 @@ -package decodex.commands; - -import decodex.data.DataManager; -import decodex.data.exception.CommandException; -import decodex.data.exception.DataManagerException; -import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeException; -import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.ModuleManagerException; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import java.io.IOException; - -/** - * Command serves as a foundation for other commands to be built upon. - */ -public abstract class Command { - - public Command() { - } - - public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) - throws CommandException, ModuleManagerException, DataManagerException, ModuleException, RecipeException, - RecipeManagerException, IOException; -} +package decodex.logic; + +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.ModuleManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import java.io.IOException; + +/** + * Command serves as a foundation for other commands to be built upon. + */ +public abstract class Command { + + public Command() { + } + + public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) + throws CommandException, ModuleManagerException, DataManagerException, ModuleException, RecipeException, + RecipeManagerException, IOException; +} diff --git a/src/main/java/decodex/commands/ExitCommand.java b/src/main/java/decodex/logic/commands/ExitCommand.java similarity index 90% rename from src/main/java/decodex/commands/ExitCommand.java rename to src/main/java/decodex/logic/commands/ExitCommand.java index b01d91217f..f3f0179445 100644 --- a/src/main/java/decodex/commands/ExitCommand.java +++ b/src/main/java/decodex/logic/commands/ExitCommand.java @@ -1,6 +1,7 @@ -package decodex.commands; +package decodex.logic.commands; import decodex.data.DataManager; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; diff --git a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeDeleteCommand.java similarity index 95% rename from src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipeDeleteCommand.java index c631f8f7df..567d25770d 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeDeleteCommand.java @@ -1,6 +1,6 @@ -package decodex.commands.recipe; +package decodex.logic.commands.recipe; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/main/java/decodex/commands/recipe/RecipeListCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeListCommand.java similarity index 97% rename from src/main/java/decodex/commands/recipe/RecipeListCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipeListCommand.java index b8b519423f..d40816e285 100644 --- a/src/main/java/decodex/commands/recipe/RecipeListCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeListCommand.java @@ -1,6 +1,6 @@ -package decodex.commands.recipe; +package decodex.logic.commands.recipe; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeNewCommand.java similarity index 95% rename from src/main/java/decodex/commands/recipe/RecipeNewCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipeNewCommand.java index d2997b2f1d..0a973840fb 100644 --- a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeNewCommand.java @@ -1,6 +1,6 @@ -package decodex.commands.recipe; +package decodex.logic.commands.recipe; -import decodex.commands.Command; +import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index ab7681868a..844278cdfd 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -2,8 +2,8 @@ import java.util.Arrays; -import decodex.commands.Command; -import decodex.commands.ExitCommand; +import decodex.logic.Command; +import decodex.logic.commands.ExitCommand; import decodex.commands.InputCommand; import decodex.commands.HelpCommand; import decodex.commands.ListCommand; diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index 50481c061b..d90ea69dbb 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -1,10 +1,10 @@ package decodex.parser; -import decodex.commands.Command; -import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.logic.Command; +import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.commands.recipe.RecipeDeselectCommand; -import decodex.commands.recipe.RecipeListCommand; -import decodex.commands.recipe.RecipeNewCommand; +import decodex.logic.commands.recipe.RecipeListCommand; +import decodex.logic.commands.recipe.RecipeNewCommand; import decodex.commands.recipe.RecipePopCommand; import decodex.commands.recipe.RecipePushCommand; import decodex.commands.recipe.RecipeResetCommand; diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java similarity index 94% rename from src/test/java/decodex/commands/RecipeDeleteCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java index b034b79b6f..071dbc26ef 100644 --- a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java @@ -1,8 +1,9 @@ -package decodex.commands; +package decodex.logic.commands.recipe; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.commands.recipe.RecipeDeleteCommand; +import decodex.logic.Command; +import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java similarity index 97% rename from src/test/java/decodex/commands/RecipeListCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java index c662ef4ee8..e2782638b5 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java @@ -1,9 +1,9 @@ -package decodex.commands; +package decodex.logic.commands.recipe; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.commands.recipe.RecipeListCommand; +import decodex.logic.commands.recipe.RecipeListCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java similarity index 94% rename from src/test/java/decodex/commands/RecipeNewCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java index f066f867f3..bfc1b7fb4d 100644 --- a/src/test/java/decodex/commands/RecipeNewCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java @@ -1,9 +1,10 @@ -package decodex.commands; +package decodex.logic.commands.recipe; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.commands.recipe.RecipeNewCommand; +import decodex.logic.Command; +import decodex.logic.commands.recipe.RecipeNewCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 2335c0846e..4fb3ed4a9f 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -1,7 +1,7 @@ package decodex.parser; -import decodex.commands.Command; -import decodex.commands.ExitCommand; +import decodex.logic.Command; +import decodex.logic.commands.ExitCommand; import decodex.commands.InputCommand; import decodex.commands.ListCommand; import decodex.commands.ResetCommand; diff --git a/src/test/java/decodex/parser/RecipeCommandParserTest.java b/src/test/java/decodex/parser/RecipeCommandParserTest.java index e5445b081c..58ab0b4e8b 100644 --- a/src/test/java/decodex/parser/RecipeCommandParserTest.java +++ b/src/test/java/decodex/parser/RecipeCommandParserTest.java @@ -1,8 +1,8 @@ package decodex.parser; -import decodex.commands.Command; -import decodex.commands.recipe.RecipeDeleteCommand; -import decodex.commands.recipe.RecipeNewCommand; +import decodex.logic.Command; +import decodex.logic.commands.recipe.RecipeDeleteCommand; +import decodex.logic.commands.recipe.RecipeNewCommand; import decodex.commands.recipe.RecipePopCommand; import decodex.commands.recipe.RecipePushCommand; import decodex.commands.recipe.RecipeResetCommand; From 258a04d903fd1261002aebcb35cf66f76448ba14 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 5 Nov 2021 15:16:52 +0800 Subject: [PATCH 474/607] Move command classes --- .../{ => logic}/commands/HelpCommand.java | 112 +++++++------- .../{ => logic}/commands/InputCommand.java | 78 +++++----- .../{ => logic}/commands/ResetCommand.java | 56 +++---- .../commands/recipe/RecipePopCommand.java | 78 +++++----- .../commands/recipe/RecipePushCommand.java | 106 ++++++------- .../commands/recipe/RecipeResetCommand.java | 74 ++++----- .../commands/recipe/RecipeSelectCommand.java | 74 ++++----- src/main/java/decodex/parser/Parser.java | 6 +- .../decodex/parser/RecipeCommandParser.java | 8 +- .../commands/RecipeDeselectCommandTest.java | 2 +- .../{ => logic}/commands/HelpCommandTest.java | 95 ++++++------ .../commands/InputCommandTest.java | 99 +++++++------ .../commands/ResetCommandTest.java | 109 +++++++------- .../recipe}/RecipePopCommandTest.java | 126 ++++++++-------- .../recipe}/RecipePushCommandTest.java | 138 ++++++++--------- .../recipe}/RecipeResetCommandTest.java | 94 ++++++------ .../recipe}/RecipeSelectCommandTest.java | 140 +++++++++--------- src/test/java/decodex/parser/ParserTest.java | 4 +- .../parser/RecipeCommandParserTest.java | 8 +- 19 files changed, 706 insertions(+), 701 deletions(-) rename src/main/java/decodex/{ => logic}/commands/HelpCommand.java (82%) rename src/main/java/decodex/{ => logic}/commands/InputCommand.java (94%) rename src/main/java/decodex/{ => logic}/commands/ResetCommand.java (93%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipePopCommand.java (94%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipePushCommand.java (95%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipeResetCommand.java (94%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipeSelectCommand.java (94%) rename src/test/java/decodex/{ => logic}/commands/HelpCommandTest.java (94%) rename src/test/java/decodex/{ => logic}/commands/InputCommandTest.java (95%) rename src/test/java/decodex/{ => logic}/commands/ResetCommandTest.java (95%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipePopCommandTest.java (94%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipePushCommandTest.java (94%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipeResetCommandTest.java (93%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipeSelectCommandTest.java (94%) diff --git a/src/main/java/decodex/commands/HelpCommand.java b/src/main/java/decodex/logic/commands/HelpCommand.java similarity index 82% rename from src/main/java/decodex/commands/HelpCommand.java rename to src/main/java/decodex/logic/commands/HelpCommand.java index c9b51f08c3..896eedf178 100644 --- a/src/main/java/decodex/commands/HelpCommand.java +++ b/src/main/java/decodex/logic/commands/HelpCommand.java @@ -1,55 +1,57 @@ -package decodex.commands; - -import decodex.logic.Command; -import decodex.logic.commands.ExitCommand; -import decodex.logic.commands.recipe.RecipeDeleteCommand; -import decodex.commands.recipe.RecipeDeselectCommand; -import decodex.logic.commands.recipe.RecipeListCommand; -import decodex.logic.commands.recipe.RecipeNewCommand; -import decodex.commands.recipe.RecipePopCommand; -import decodex.commands.recipe.RecipePushCommand; -import decodex.commands.recipe.RecipeResetCommand; -import decodex.commands.recipe.RecipeSelectCommand; -import decodex.data.DataManager; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; - -// @@author rizemon -public class HelpCommand extends Command { - - public static final String COMMAND_WORD = "help"; - public static final String COMMAND_HELP_MESSAGE = "Shows list of available commands\n" - + "Syntax: help"; - - private static final String[] BASIC_COMMAND_HELP_MESSAGES = { - HelpCommand.COMMAND_HELP_MESSAGE, - ExitCommand.COMMAND_HELP_MESSAGE, - InputCommand.COMMAND_HELP_MESSAGE, - ListCommand.COMMAND_HELP_MESSAGE, - SelectCommand.COMMAND_HELP_MESSAGE, - ShowCommand.COMMAND_HELP_MESSAGE - }; - - private static final String[] RECIPE_COMMAND_HELP_MESSAGES = { - RecipeNewCommand.COMMAND_HELP_MESSAGE, - RecipeListCommand.COMMAND_HELP_MESSAGE, - RecipeSelectCommand.COMMAND_HELP_MESSAGE, - RecipeDeselectCommand.COMMAND_HELP_MESSAGE, - RecipePushCommand.COMMAND_HELP_MESSAGE, - RecipePopCommand.COMMAND_HELP_MESSAGE, - RecipeResetCommand.COMMAND_HELP_MESSAGE, - RecipeDeleteCommand.COMMAND_HELP_MESSAGE, - }; - - public HelpCommand() { - super(); - } - - @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) { - ui.printCommandHelp(BASIC_COMMAND_HELP_MESSAGES, RECIPE_COMMAND_HELP_MESSAGES); - } -} +package decodex.logic.commands; + +import decodex.commands.ListCommand; +import decodex.commands.SelectCommand; +import decodex.commands.ShowCommand; +import decodex.logic.Command; +import decodex.logic.commands.recipe.RecipeDeleteCommand; +import decodex.commands.recipe.RecipeDeselectCommand; +import decodex.logic.commands.recipe.RecipeListCommand; +import decodex.logic.commands.recipe.RecipeNewCommand; +import decodex.logic.commands.recipe.RecipePopCommand; +import decodex.logic.commands.recipe.RecipePushCommand; +import decodex.logic.commands.recipe.RecipeResetCommand; +import decodex.logic.commands.recipe.RecipeSelectCommand; +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; + +// @@author rizemon +public class HelpCommand extends Command { + + public static final String COMMAND_WORD = "help"; + public static final String COMMAND_HELP_MESSAGE = "Shows list of available commands\n" + + "Syntax: help"; + + private static final String[] BASIC_COMMAND_HELP_MESSAGES = { + HelpCommand.COMMAND_HELP_MESSAGE, + ExitCommand.COMMAND_HELP_MESSAGE, + InputCommand.COMMAND_HELP_MESSAGE, + ListCommand.COMMAND_HELP_MESSAGE, + SelectCommand.COMMAND_HELP_MESSAGE, + ShowCommand.COMMAND_HELP_MESSAGE + }; + + private static final String[] RECIPE_COMMAND_HELP_MESSAGES = { + RecipeNewCommand.COMMAND_HELP_MESSAGE, + RecipeListCommand.COMMAND_HELP_MESSAGE, + RecipeSelectCommand.COMMAND_HELP_MESSAGE, + RecipeDeselectCommand.COMMAND_HELP_MESSAGE, + RecipePushCommand.COMMAND_HELP_MESSAGE, + RecipePopCommand.COMMAND_HELP_MESSAGE, + RecipeResetCommand.COMMAND_HELP_MESSAGE, + RecipeDeleteCommand.COMMAND_HELP_MESSAGE, + }; + + public HelpCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) { + ui.printCommandHelp(BASIC_COMMAND_HELP_MESSAGES, RECIPE_COMMAND_HELP_MESSAGES); + } +} diff --git a/src/main/java/decodex/commands/InputCommand.java b/src/main/java/decodex/logic/commands/InputCommand.java similarity index 94% rename from src/main/java/decodex/commands/InputCommand.java rename to src/main/java/decodex/logic/commands/InputCommand.java index c0f1bd192c..d47f533bd1 100644 --- a/src/main/java/decodex/commands/InputCommand.java +++ b/src/main/java/decodex/logic/commands/InputCommand.java @@ -1,39 +1,39 @@ -package decodex.commands; - -import decodex.data.Data; -import decodex.data.DataManager; -import decodex.data.exception.CommandException; -import decodex.logic.Command; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import decodex.ui.messages.ErrorMessages; - -// @@author rizemon -public class InputCommand extends Command { - - public static final String COMMAND_WORD = "input"; - public static final String COMMAND_HELP_MESSAGE = "Input of data\n" - + "Syntax: input "; - - private final String dataString; - - public InputCommand(String dataString) { - super(); - this.dataString = dataString; - } - - @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws CommandException { - if (dataString.isEmpty()) { - throw new CommandException(ErrorMessages.MISSING_ARGUMENT); - } - - Data userData = new Data(dataString); - dataManager.setOriginalData(userData); - - ui.printInput(dataString); - } -} +package decodex.logic.commands; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.logic.Command; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; + +// @@author rizemon +public class InputCommand extends Command { + + public static final String COMMAND_WORD = "input"; + public static final String COMMAND_HELP_MESSAGE = "Input of data\n" + + "Syntax: input "; + + private final String dataString; + + public InputCommand(String dataString) { + super(); + this.dataString = dataString; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException { + if (dataString.isEmpty()) { + throw new CommandException(ErrorMessages.MISSING_ARGUMENT); + } + + Data userData = new Data(dataString); + dataManager.setOriginalData(userData); + + ui.printInput(dataString); + } +} diff --git a/src/main/java/decodex/commands/ResetCommand.java b/src/main/java/decodex/logic/commands/ResetCommand.java similarity index 93% rename from src/main/java/decodex/commands/ResetCommand.java rename to src/main/java/decodex/logic/commands/ResetCommand.java index 07bfd0280a..da8b38b86f 100644 --- a/src/main/java/decodex/commands/ResetCommand.java +++ b/src/main/java/decodex/logic/commands/ResetCommand.java @@ -1,28 +1,28 @@ -package decodex.commands; - -import decodex.data.DataManager; -import decodex.logic.Command; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import decodex.ui.messages.RegularMessages; - -// @@author rizemon -public class ResetCommand extends Command { - - public static final String COMMAND_WORD = "reset"; - public static final String COMMAND_HELP_MESSAGE = "Resets the data\n" - + "Syntax: reset\n"; - - public ResetCommand() { - super(); - } - - @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) { - dataManager.resetToOriginalData(); - ui.printSuccess(RegularMessages.REVERTED_ALL_CHANGES); - } -} +package decodex.logic.commands; + +import decodex.data.DataManager; +import decodex.logic.Command; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import decodex.ui.messages.RegularMessages; + +// @@author rizemon +public class ResetCommand extends Command { + + public static final String COMMAND_WORD = "reset"; + public static final String COMMAND_HELP_MESSAGE = "Resets the data\n" + + "Syntax: reset\n"; + + public ResetCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) { + dataManager.resetToOriginalData(); + ui.printSuccess(RegularMessages.REVERTED_ALL_CHANGES); + } +} diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipePopCommand.java similarity index 94% rename from src/main/java/decodex/commands/recipe/RecipePopCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipePopCommand.java index 97d9559258..36d9c42aae 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipePopCommand.java @@ -1,39 +1,39 @@ -package decodex.commands.recipe; - -import decodex.logic.Command; -import decodex.data.DataManager; -import decodex.data.exception.RecipeException; -import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.StorageException; -import decodex.modules.Module; -import decodex.modules.ModuleManager; -import decodex.recipes.Recipe; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import java.io.IOException; - -// @@author rizemon -public class RecipePopCommand extends Command { - - public static final String COMMAND_WORD = "pop"; - public static final String COMMAND_HELP_MESSAGE = "Remove module from recipe\n" - + "Syntax: recipe pop"; - - public RecipePopCommand() { - super(); - } - - @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws RecipeException, RecipeManagerException, IOException { - Module module = recipeManager.popModuleFromEditedRecipe(); - Recipe editingRecipe = recipeManager.getEditingRecipe(); - try { - storage.saveRecipeToFile(editingRecipe); - } catch (IOException | StorageException err) { - ui.printError(err); - } - ui.printModuleRemovedFromRecipe(module.getName(), editingRecipe.getName()); - } -} +package decodex.logic.commands.recipe; + +import decodex.logic.Command; +import decodex.data.DataManager; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import java.io.IOException; + +// @@author rizemon +public class RecipePopCommand extends Command { + + public static final String COMMAND_WORD = "pop"; + public static final String COMMAND_HELP_MESSAGE = "Remove module from recipe\n" + + "Syntax: recipe pop"; + + public RecipePopCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws RecipeException, RecipeManagerException, IOException { + Module module = recipeManager.popModuleFromEditedRecipe(); + Recipe editingRecipe = recipeManager.getEditingRecipe(); + try { + storage.saveRecipeToFile(editingRecipe); + } catch (IOException | StorageException err) { + ui.printError(err); + } + ui.printModuleRemovedFromRecipe(module.getName(), editingRecipe.getName()); + } +} diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipePushCommand.java similarity index 95% rename from src/main/java/decodex/commands/recipe/RecipePushCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipePushCommand.java index 7d6921fc98..0fb69d9f8c 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipePushCommand.java @@ -1,53 +1,53 @@ -package decodex.commands.recipe; - -import decodex.logic.Command; -import decodex.data.DataManager; -import decodex.data.exception.CommandException; -import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.ModuleManagerException; -import decodex.data.exception.StorageException; -import decodex.modules.Module; -import decodex.modules.ModuleManager; -import decodex.recipes.Recipe; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import decodex.ui.messages.ErrorMessages; -import java.io.IOException; - -// @@author rizemon -public class RecipePushCommand extends Command { - - public static final String COMMAND_WORD = "push"; - public static final String COMMAND_HELP_MESSAGE = "Add module to recipe\n" - + "Syntax: recipe push {moduleArgument}"; - - private final String moduleName; - private final String[] parameters; - - public RecipePushCommand(String moduleName, String[] parameters) { - super(); - this.moduleName = moduleName; - this.parameters = parameters; - } - - @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException, - IOException { - if (moduleName.isBlank()) { - throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); - } - - Module module = moduleManager.selectModule(moduleName, parameters); - Recipe editingRecipe = recipeManager.getEditingRecipe(); - recipeManager.pushModuleIntoEditedRecipe(module); - try { - storage.saveRecipeToFile(editingRecipe); - } catch (IOException | StorageException err) { - ui.printError(err); - } - ui.printModuleAddedToRecipe(module.getName(), editingRecipe.getName()); - } -} +package decodex.logic.commands.recipe; + +import decodex.logic.Command; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.StorageException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; +import java.io.IOException; + +// @@author rizemon +public class RecipePushCommand extends Command { + + public static final String COMMAND_WORD = "push"; + public static final String COMMAND_HELP_MESSAGE = "Add module to recipe\n" + + "Syntax: recipe push {moduleArgument}"; + + private final String moduleName; + private final String[] parameters; + + public RecipePushCommand(String moduleName, String[] parameters) { + super(); + this.moduleName = moduleName; + this.parameters = parameters; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException, + IOException { + if (moduleName.isBlank()) { + throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); + } + + Module module = moduleManager.selectModule(moduleName, parameters); + Recipe editingRecipe = recipeManager.getEditingRecipe(); + recipeManager.pushModuleIntoEditedRecipe(module); + try { + storage.saveRecipeToFile(editingRecipe); + } catch (IOException | StorageException err) { + ui.printError(err); + } + ui.printModuleAddedToRecipe(module.getName(), editingRecipe.getName()); + } +} diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeResetCommand.java similarity index 94% rename from src/main/java/decodex/commands/recipe/RecipeResetCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipeResetCommand.java index bfb6273997..4f2d13738e 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeResetCommand.java @@ -1,37 +1,37 @@ -package decodex.commands.recipe; - -import decodex.logic.Command; -import decodex.data.DataManager; -import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.StorageException; -import decodex.modules.ModuleManager; -import decodex.recipes.Recipe; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import java.io.IOException; - -// @@author rizemon -public class RecipeResetCommand extends Command { - - public static final String COMMAND_WORD = "reset"; - public static final String COMMAND_HELP_MESSAGE = "Clear all modules from recipe\n" - + "Syntax: recipe reset"; - - public RecipeResetCommand() { - super(); - } - - @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws RecipeManagerException, IOException { - Recipe editingRecipe = recipeManager.getEditingRecipe(); - recipeManager.resetEditedRecipe(); - try { - storage.saveRecipeToFile(editingRecipe); - } catch (IOException | StorageException err) { - ui.printError(err); - } - ui.printRecipeReset(editingRecipe.getName()); - } -} +package decodex.logic.commands.recipe; + +import decodex.logic.Command; +import decodex.data.DataManager; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.StorageException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import java.io.IOException; + +// @@author rizemon +public class RecipeResetCommand extends Command { + + public static final String COMMAND_WORD = "reset"; + public static final String COMMAND_HELP_MESSAGE = "Clear all modules from recipe\n" + + "Syntax: recipe reset"; + + public RecipeResetCommand() { + super(); + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws RecipeManagerException, IOException { + Recipe editingRecipe = recipeManager.getEditingRecipe(); + recipeManager.resetEditedRecipe(); + try { + storage.saveRecipeToFile(editingRecipe); + } catch (IOException | StorageException err) { + ui.printError(err); + } + ui.printRecipeReset(editingRecipe.getName()); + } +} diff --git a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeSelectCommand.java similarity index 94% rename from src/main/java/decodex/commands/recipe/RecipeSelectCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipeSelectCommand.java index 2ba2bb1e60..8e3352ca84 100644 --- a/src/main/java/decodex/commands/recipe/RecipeSelectCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeSelectCommand.java @@ -1,37 +1,37 @@ -package decodex.commands.recipe; - -import decodex.logic.Command; -import decodex.data.DataManager; -import decodex.data.exception.CommandException; -import decodex.data.exception.RecipeManagerException; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import decodex.ui.messages.ErrorMessages; - -// @@author rizemon -public class RecipeSelectCommand extends Command { - - public static final String COMMAND_WORD = "select"; - public static final String COMMAND_HELP_MESSAGE = "Select recipe for editing\n" - + "Syntax: recipe select "; - - private final String recipeName; - - public RecipeSelectCommand(String recipeName) { - super(); - this.recipeName = recipeName; - } - - @Override - public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, - Storage storage) throws CommandException, RecipeManagerException { - if (recipeName.isBlank()) { - throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); - } - - recipeManager.selectRecipeForEditing(recipeName); - ui.printRecipeSelected(recipeName); - } -} +package decodex.logic.commands.recipe; + +import decodex.logic.Command; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import decodex.ui.messages.ErrorMessages; + +// @@author rizemon +public class RecipeSelectCommand extends Command { + + public static final String COMMAND_WORD = "select"; + public static final String COMMAND_HELP_MESSAGE = "Select recipe for editing\n" + + "Syntax: recipe select "; + + private final String recipeName; + + public RecipeSelectCommand(String recipeName) { + super(); + this.recipeName = recipeName; + } + + @Override + public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager, + Storage storage) throws CommandException, RecipeManagerException { + if (recipeName.isBlank()) { + throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); + } + + recipeManager.selectRecipeForEditing(recipeName); + ui.printRecipeSelected(recipeName); + } +} diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 844278cdfd..3e164b986d 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -4,10 +4,10 @@ import decodex.logic.Command; import decodex.logic.commands.ExitCommand; -import decodex.commands.InputCommand; -import decodex.commands.HelpCommand; +import decodex.logic.commands.InputCommand; +import decodex.logic.commands.HelpCommand; import decodex.commands.ListCommand; -import decodex.commands.ResetCommand; +import decodex.logic.commands.ResetCommand; import decodex.commands.SelectCommand; import decodex.commands.ShowCommand; import decodex.data.exception.CommandException; diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index d90ea69dbb..233e1c0e17 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -5,10 +5,10 @@ import decodex.commands.recipe.RecipeDeselectCommand; import decodex.logic.commands.recipe.RecipeListCommand; import decodex.logic.commands.recipe.RecipeNewCommand; -import decodex.commands.recipe.RecipePopCommand; -import decodex.commands.recipe.RecipePushCommand; -import decodex.commands.recipe.RecipeResetCommand; -import decodex.commands.recipe.RecipeSelectCommand; +import decodex.logic.commands.recipe.RecipePopCommand; +import decodex.logic.commands.recipe.RecipePushCommand; +import decodex.logic.commands.recipe.RecipeResetCommand; +import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.exception.CommandException; import decodex.ui.messages.ErrorMessages; import java.util.Arrays; diff --git a/src/test/java/decodex/commands/RecipeDeselectCommandTest.java b/src/test/java/decodex/commands/RecipeDeselectCommandTest.java index 72c11a64b4..d985b4f8ae 100644 --- a/src/test/java/decodex/commands/RecipeDeselectCommandTest.java +++ b/src/test/java/decodex/commands/RecipeDeselectCommandTest.java @@ -3,7 +3,7 @@ import java.io.IOException; import decodex.commands.recipe.RecipeDeselectCommand; -import decodex.commands.recipe.RecipeSelectCommand; +import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/test/java/decodex/commands/HelpCommandTest.java b/src/test/java/decodex/logic/commands/HelpCommandTest.java similarity index 94% rename from src/test/java/decodex/commands/HelpCommandTest.java rename to src/test/java/decodex/logic/commands/HelpCommandTest.java index 73e1611836..02808b5ad1 100644 --- a/src/test/java/decodex/commands/HelpCommandTest.java +++ b/src/test/java/decodex/logic/commands/HelpCommandTest.java @@ -1,47 +1,48 @@ -package decodex.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; - -import decodex.storage.Storage; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - -import decodex.data.DataManager; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; -import decodex.ui.Ui; - -// @@author rizemon -class HelpCommandTest { - - private ByteArrayOutputStream outputStream; - private final PrintStream originalOutputStream = System.out; - - private final DataManager dataManager = new DataManager(); - private final ModuleManager moduleManager = new ModuleManager(); - private final Storage storage = new Storage(); - private final RecipeManager recipeManager = new RecipeManager(); - private final Ui ui = new Ui(); - - @BeforeEach - public void setOutputStream() { - outputStream = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outputStream)); - } - - @AfterEach - public void restoreOutputStream() { - System.setOut(originalOutputStream); - } - - @Test - public void run_allCommands_success() { - HelpCommand command = new HelpCommand(); - command.run(dataManager, moduleManager, ui, recipeManager, storage); - assertFalse(outputStream.toString().isBlank()); - } -} +package decodex.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import decodex.logic.commands.HelpCommand; +import decodex.storage.Storage; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import decodex.data.DataManager; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +// @@author rizemon +class HelpCommandTest { + + private ByteArrayOutputStream outputStream; + private final PrintStream originalOutputStream = System.out; + + private final DataManager dataManager = new DataManager(); + private final ModuleManager moduleManager = new ModuleManager(); + private final Storage storage = new Storage(); + private final RecipeManager recipeManager = new RecipeManager(); + private final Ui ui = new Ui(); + + @BeforeEach + public void setOutputStream() { + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + } + + @AfterEach + public void restoreOutputStream() { + System.setOut(originalOutputStream); + } + + @Test + public void run_allCommands_success() { + HelpCommand command = new HelpCommand(); + command.run(dataManager, moduleManager, ui, recipeManager, storage); + assertFalse(outputStream.toString().isBlank()); + } +} diff --git a/src/test/java/decodex/commands/InputCommandTest.java b/src/test/java/decodex/logic/commands/InputCommandTest.java similarity index 95% rename from src/test/java/decodex/commands/InputCommandTest.java rename to src/test/java/decodex/logic/commands/InputCommandTest.java index fc5c842185..cda9cfdb39 100644 --- a/src/test/java/decodex/commands/InputCommandTest.java +++ b/src/test/java/decodex/logic/commands/InputCommandTest.java @@ -1,49 +1,50 @@ -package decodex.commands; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import decodex.storage.Storage; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - -import decodex.data.Data; -import decodex.data.DataManager; -import decodex.data.exception.CommandException; -import decodex.data.exception.DataManagerException; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; -import decodex.ui.Ui; - -// @@author rizemon -public class InputCommandTest { - - @Test - public void run_missingArguments_expectException() { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - String dataString = ""; - InputCommand testCommand = new InputCommand(dataString); - assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, - storage)); - } - - @Test - public void run_insertData_insertedData() - throws CommandException, DataManagerException { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - String dataString = "something"; - InputCommand testCommand = new InputCommand(dataString); - testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); - Data testData = new Data(dataString); - assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); - } -} +package decodex.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.logic.commands.InputCommand; +import decodex.storage.Storage; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.DataManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +// @@author rizemon +public class InputCommandTest { + + @Test + public void run_missingArguments_expectException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + String dataString = ""; + InputCommand testCommand = new InputCommand(dataString); + assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); + } + + @Test + public void run_insertData_insertedData() + throws CommandException, DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + String dataString = "something"; + InputCommand testCommand = new InputCommand(dataString); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + Data testData = new Data(dataString); + assertTrue(Arrays.equals(dataManager.getOriginalData().getRawBytes(), testData.getRawBytes())); + } +} diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/logic/commands/ResetCommandTest.java similarity index 95% rename from src/test/java/decodex/commands/ResetCommandTest.java rename to src/test/java/decodex/logic/commands/ResetCommandTest.java index 32495de42f..d364df51e3 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/logic/commands/ResetCommandTest.java @@ -1,54 +1,55 @@ -package decodex.commands; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import decodex.storage.Storage; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - -import decodex.data.Data; -import decodex.data.DataManager; -import decodex.data.exception.DataManagerException; -import decodex.modules.ModuleManager; -import decodex.recipes.RecipeManager; - -import decodex.ui.Ui; - -// @@author rizemon -class ResetCommandTest { - - @Test - public void run_noChanges_sameOriginalData() throws DataManagerException { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - - Data originalData = new Data("hi"); - dataManager.setOriginalData(originalData); - - ResetCommand testCommand = new ResetCommand(); - testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); - assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); - } - - @Test - public void run_newData_sameOriginalData() throws DataManagerException { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - - Data originalData = new Data("hi"); - dataManager.setOriginalData(originalData); - Data newData = new Data("bye"); - dataManager.setCurrentData(newData); - - ResetCommand testCommand = new ResetCommand(); - testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); - assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); - } -} +package decodex.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import decodex.logic.commands.ResetCommand; +import decodex.storage.Storage; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import decodex.data.Data; +import decodex.data.DataManager; +import decodex.data.exception.DataManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.RecipeManager; + +import decodex.ui.Ui; + +// @@author rizemon +class ResetCommandTest { + + @Test + public void run_noChanges_sameOriginalData() throws DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + Data originalData = new Data("hi"); + dataManager.setOriginalData(originalData); + + ResetCommand testCommand = new ResetCommand(); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); + } + + @Test + public void run_newData_sameOriginalData() throws DataManagerException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + Data originalData = new Data("hi"); + dataManager.setOriginalData(originalData); + Data newData = new Data("bye"); + dataManager.setCurrentData(newData); + + ResetCommand testCommand = new ResetCommand(); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + assertTrue(Arrays.equals(dataManager.getCurrentData().getRawBytes(), originalData.getRawBytes())); + } +} diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipePopCommandTest.java similarity index 94% rename from src/test/java/decodex/commands/RecipePopCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipePopCommandTest.java index cdc5d735cc..1bc4e0febe 100644 --- a/src/test/java/decodex/commands/RecipePopCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipePopCommandTest.java @@ -1,63 +1,63 @@ -package decodex.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import decodex.commands.recipe.RecipePopCommand; -import decodex.data.DataManager; -import decodex.data.exception.ModuleException; -import decodex.data.exception.ModuleManagerException; -import decodex.data.exception.RecipeException; -import decodex.data.exception.RecipeManagerException; -import decodex.modules.Module; -import decodex.modules.ModuleManager; -import decodex.recipes.Recipe; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import java.io.IOException; -import org.junit.jupiter.api.Test; - -// @@author rizemon -class RecipePopCommandTest { - - @Test - public void run_oneModuleToEditingRecipe_recipeSizeIsZero() - throws RecipeManagerException, ModuleManagerException, ModuleException, RecipeException, IOException { - RecipeManager recipeManager = new RecipeManager(); - String testRecipeName = "test"; - Recipe testRecipe = new Recipe(testRecipeName); - recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipeForEditing(testRecipeName); - - ModuleManager moduleManager = new ModuleManager(); - String moduleName = "base64encode"; - String[] parameters = {}; - Module module = moduleManager.selectModule(moduleName, parameters); - testRecipe.push(module); - - RecipePopCommand testCommand = new RecipePopCommand(); - DataManager dataManager = new DataManager(); - Ui ui = new Ui(); - Storage storage = new Storage(); - testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); - - assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); - } - - @Test - public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException, IOException { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - Storage storage = new Storage(); - String testRecipeName = "test"; - Recipe testRecipe = new Recipe(testRecipeName); - recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipeForEditing(testRecipeName); - RecipePopCommand testCommand = new RecipePopCommand(); - assertThrows(RecipeException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, - storage)); - } -} +package decodex.logic.commands.recipe; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.logic.commands.recipe.RecipePopCommand; +import decodex.data.DataManager; +import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.Module; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +// @@author rizemon +class RecipePopCommandTest { + + @Test + public void run_oneModuleToEditingRecipe_recipeSizeIsZero() + throws RecipeManagerException, ModuleManagerException, ModuleException, RecipeException, IOException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + ModuleManager moduleManager = new ModuleManager(); + String moduleName = "base64encode"; + String[] parameters = {}; + Module module = moduleManager.selectModule(moduleName, parameters); + testRecipe.push(module); + + RecipePopCommand testCommand = new RecipePopCommand(); + DataManager dataManager = new DataManager(); + Ui ui = new Ui(); + Storage storage = new Storage(); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); + } + + @Test + public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException, IOException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + Storage storage = new Storage(); + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + RecipePopCommand testCommand = new RecipePopCommand(); + assertThrows(RecipeException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); + } +} diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipePushCommandTest.java similarity index 94% rename from src/test/java/decodex/commands/RecipePushCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipePushCommandTest.java index 11a32a0cd8..ed462a923e 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipePushCommandTest.java @@ -1,69 +1,69 @@ -package decodex.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import decodex.commands.recipe.RecipePushCommand; -import decodex.data.DataManager; -import decodex.data.exception.CommandException; -import decodex.data.exception.ModuleException; -import decodex.data.exception.ModuleManagerException; -import decodex.data.exception.RecipeException; -import decodex.data.exception.RecipeManagerException; -import decodex.modules.ModuleManager; -import decodex.recipes.Recipe; -import decodex.recipes.RecipeManager; -import decodex.storage.Storage; -import decodex.ui.Ui; -import java.io.IOException; -import org.junit.jupiter.api.Test; - -// @@author rizemon -class RecipePushCommandTest { - - @Test - public void run_oneModuleToEditingRecipe_recipeSizeIsOne() - throws RecipeException, RecipeManagerException, CommandException, - ModuleException, ModuleManagerException, IOException { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - - String testRecipeName = "test"; - Recipe testRecipe = new Recipe(testRecipeName); - recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipeForEditing(testRecipeName); - - String moduleName = "base64encode"; - String[] parameters = {}; - - RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); - testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); - - assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 1); - } - - @Test - public void run_emptyModuleName_expectException() throws RecipeException, RecipeManagerException, IOException { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - - String testRecipeName = "test"; - Recipe testRecipe = new Recipe(testRecipeName); - recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipeForEditing(testRecipeName); - - String moduleName = ""; - String[] parameters = {}; - - RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); - - assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, - storage)); - } -} +package decodex.logic.commands.recipe; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.logic.commands.recipe.RecipePushCommand; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.storage.Storage; +import decodex.ui.Ui; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +// @@author rizemon +class RecipePushCommandTest { + + @Test + public void run_oneModuleToEditingRecipe_recipeSizeIsOne() + throws RecipeException, RecipeManagerException, CommandException, + ModuleException, ModuleManagerException, IOException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + String moduleName = "base64encode"; + String[] parameters = {}; + + RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 1); + } + + @Test + public void run_emptyModuleName_expectException() throws RecipeException, RecipeManagerException, IOException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + String moduleName = ""; + String[] parameters = {}; + + RecipePushCommand testCommand = new RecipePushCommand(moduleName, parameters); + + assertThrows(CommandException.class, () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, + storage)); + } +} diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeResetCommandTest.java similarity index 93% rename from src/test/java/decodex/commands/RecipeResetCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipeResetCommandTest.java index b9850e6191..ed531314b1 100644 --- a/src/test/java/decodex/commands/RecipeResetCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeResetCommandTest.java @@ -1,47 +1,47 @@ -package decodex.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import decodex.data.exception.RecipeException; -import decodex.storage.Storage; -import java.io.IOException; -import org.junit.jupiter.api.Test; - -import decodex.commands.recipe.RecipeResetCommand; -import decodex.data.DataManager; -import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.ModuleManagerException; -import decodex.modules.ModuleManager; -import decodex.modules.Module; -import decodex.recipes.Recipe; -import decodex.recipes.RecipeManager; -import decodex.ui.Ui; - -// @@author rizemon -class RecipeResetCommandTest { - - @Test - public void run_oneModuleInEditingRecipe_recipeSizeIsZero() throws RecipeException, RecipeManagerException, - ModuleManagerException, ModuleException, IOException { - RecipeManager recipeManager = new RecipeManager(); - String testRecipeName = "test"; - Recipe testRecipe = new Recipe(testRecipeName); - recipeManager.addRecipe(testRecipe); - recipeManager.selectRecipeForEditing(testRecipeName); - - ModuleManager moduleManager = new ModuleManager(); - String moduleName = "base64encode"; - String[] parameters = {}; - Module module = moduleManager.selectModule(moduleName, parameters); - testRecipe.push(module); - - RecipeResetCommand testCommand = new RecipeResetCommand(); - DataManager dataManager = new DataManager(); - Ui ui = new Ui(); - Storage storage = new Storage(); - testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); - - assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); - } -} +package decodex.logic.commands.recipe; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import decodex.data.exception.RecipeException; +import decodex.storage.Storage; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import decodex.logic.commands.recipe.RecipeResetCommand; +import decodex.data.DataManager; +import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeManagerException; +import decodex.data.exception.ModuleManagerException; +import decodex.modules.ModuleManager; +import decodex.modules.Module; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +// @@author rizemon +class RecipeResetCommandTest { + + @Test + public void run_oneModuleInEditingRecipe_recipeSizeIsZero() throws RecipeException, RecipeManagerException, + ModuleManagerException, ModuleException, IOException { + RecipeManager recipeManager = new RecipeManager(); + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + recipeManager.selectRecipeForEditing(testRecipeName); + + ModuleManager moduleManager = new ModuleManager(); + String moduleName = "base64encode"; + String[] parameters = {}; + Module module = moduleManager.selectModule(moduleName, parameters); + testRecipe.push(module); + + RecipeResetCommand testCommand = new RecipeResetCommand(); + DataManager dataManager = new DataManager(); + Ui ui = new Ui(); + Storage storage = new Storage(); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + + assertEquals(recipeManager.getEditingRecipe().getModuleList().size(), 0); + } +} diff --git a/src/test/java/decodex/commands/RecipeSelectCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeSelectCommandTest.java similarity index 94% rename from src/test/java/decodex/commands/RecipeSelectCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipeSelectCommandTest.java index 30401ca9f0..47cdd340bf 100644 --- a/src/test/java/decodex/commands/RecipeSelectCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeSelectCommandTest.java @@ -1,70 +1,70 @@ -package decodex.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import decodex.data.exception.RecipeException; -import decodex.storage.Storage; -import java.io.IOException; -import org.junit.jupiter.api.Test; - -import decodex.commands.recipe.RecipeSelectCommand; -import decodex.data.DataManager; -import decodex.data.exception.CommandException; -import decodex.data.exception.RecipeManagerException; -import decodex.modules.ModuleManager; -import decodex.recipes.Recipe; -import decodex.recipes.RecipeManager; -import decodex.ui.Ui; - -// @@author rizemon -class RecipeSelectCommandTest { - - @Test - public void run_selectExistingRecipe_existingRecipe() throws RecipeException, RecipeManagerException, - CommandException, IOException { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - - String testRecipeName = "test"; - Recipe testRecipe = new Recipe(testRecipeName); - recipeManager.addRecipe(testRecipe); - - RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); - testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); - assertEquals(recipeManager.getEditingRecipe().getName(), testRecipeName); - } - - @Test - public void run_selectNonExistentRecipe_expectException() { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - - String testRecipeName = "test"; - - RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); - assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); - } - - @Test - public void run_nullRecipe_expectException() { - DataManager dataManager = new DataManager(); - ModuleManager moduleManager = new ModuleManager(); - Storage storage = new Storage(); - RecipeManager recipeManager = new RecipeManager(); - Ui ui = new Ui(); - - String testRecipeName = ""; - - RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); - assertThrows(CommandException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); - } -} +package decodex.logic.commands.recipe; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import decodex.data.exception.RecipeException; +import decodex.storage.Storage; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import decodex.logic.commands.recipe.RecipeSelectCommand; +import decodex.data.DataManager; +import decodex.data.exception.CommandException; +import decodex.data.exception.RecipeManagerException; +import decodex.modules.ModuleManager; +import decodex.recipes.Recipe; +import decodex.recipes.RecipeManager; +import decodex.ui.Ui; + +// @@author rizemon +class RecipeSelectCommandTest { + + @Test + public void run_selectExistingRecipe_existingRecipe() throws RecipeException, RecipeManagerException, + CommandException, IOException { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + Recipe testRecipe = new Recipe(testRecipeName); + recipeManager.addRecipe(testRecipe); + + RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); + testCommand.run(dataManager, moduleManager, ui, recipeManager, storage); + assertEquals(recipeManager.getEditingRecipe().getName(), testRecipeName); + } + + @Test + public void run_selectNonExistentRecipe_expectException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = "test"; + + RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); + assertThrows(RecipeManagerException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); + } + + @Test + public void run_nullRecipe_expectException() { + DataManager dataManager = new DataManager(); + ModuleManager moduleManager = new ModuleManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(); + Ui ui = new Ui(); + + String testRecipeName = ""; + + RecipeSelectCommand testCommand = new RecipeSelectCommand(testRecipeName); + assertThrows(CommandException.class, + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); + } +} diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index 4fb3ed4a9f..ce5304dcbf 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -2,9 +2,9 @@ import decodex.logic.Command; import decodex.logic.commands.ExitCommand; -import decodex.commands.InputCommand; +import decodex.logic.commands.InputCommand; import decodex.commands.ListCommand; -import decodex.commands.ResetCommand; +import decodex.logic.commands.ResetCommand; import decodex.commands.SelectCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; diff --git a/src/test/java/decodex/parser/RecipeCommandParserTest.java b/src/test/java/decodex/parser/RecipeCommandParserTest.java index 58ab0b4e8b..02ee8674fb 100644 --- a/src/test/java/decodex/parser/RecipeCommandParserTest.java +++ b/src/test/java/decodex/parser/RecipeCommandParserTest.java @@ -3,10 +3,10 @@ import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.logic.commands.recipe.RecipeNewCommand; -import decodex.commands.recipe.RecipePopCommand; -import decodex.commands.recipe.RecipePushCommand; -import decodex.commands.recipe.RecipeResetCommand; -import decodex.commands.recipe.RecipeSelectCommand; +import decodex.logic.commands.recipe.RecipePopCommand; +import decodex.logic.commands.recipe.RecipePushCommand; +import decodex.logic.commands.recipe.RecipeResetCommand; +import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.exception.CommandException; import org.junit.jupiter.api.BeforeEach; From dda37a8bddeef00f4723966245c4bd8864d7b904 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 5 Nov 2021 15:37:50 +0800 Subject: [PATCH 475/607] Move show command --- src/main/java/decodex/logic/commands/HelpCommand.java | 1 - src/main/java/decodex/{ => logic}/commands/ShowCommand.java | 2 +- src/main/java/decodex/logic/parser/Dummy.txt | 0 src/main/java/decodex/parser/Parser.java | 2 +- 4 files changed, 2 insertions(+), 3 deletions(-) rename src/main/java/decodex/{ => logic}/commands/ShowCommand.java (97%) create mode 100644 src/main/java/decodex/logic/parser/Dummy.txt diff --git a/src/main/java/decodex/logic/commands/HelpCommand.java b/src/main/java/decodex/logic/commands/HelpCommand.java index 896eedf178..afb9c64e31 100644 --- a/src/main/java/decodex/logic/commands/HelpCommand.java +++ b/src/main/java/decodex/logic/commands/HelpCommand.java @@ -2,7 +2,6 @@ import decodex.commands.ListCommand; import decodex.commands.SelectCommand; -import decodex.commands.ShowCommand; import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.commands.recipe.RecipeDeselectCommand; diff --git a/src/main/java/decodex/commands/ShowCommand.java b/src/main/java/decodex/logic/commands/ShowCommand.java similarity index 97% rename from src/main/java/decodex/commands/ShowCommand.java rename to src/main/java/decodex/logic/commands/ShowCommand.java index 447910d788..a3207343f5 100644 --- a/src/main/java/decodex/commands/ShowCommand.java +++ b/src/main/java/decodex/logic/commands/ShowCommand.java @@ -1,4 +1,4 @@ -package decodex.commands; +package decodex.logic.commands; import decodex.data.Data; import decodex.data.DataManager; diff --git a/src/main/java/decodex/logic/parser/Dummy.txt b/src/main/java/decodex/logic/parser/Dummy.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index 3e164b986d..a47cab33fb 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -9,7 +9,7 @@ import decodex.commands.ListCommand; import decodex.logic.commands.ResetCommand; import decodex.commands.SelectCommand; -import decodex.commands.ShowCommand; +import decodex.logic.commands.ShowCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; import decodex.ui.messages.ErrorMessages; From f6edd5b4ece9e984dbac2f97e19540524701781d Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Fri, 5 Nov 2021 15:42:31 +0800 Subject: [PATCH 476/607] Move commands --- src/main/java/decodex/logic/commands/HelpCommand.java | 4 +--- src/main/java/decodex/{ => logic}/commands/ListCommand.java | 2 +- src/main/java/decodex/{ => logic}/commands/SelectCommand.java | 2 +- .../{ => logic}/commands/recipe/RecipeDeselectCommand.java | 2 +- src/main/java/decodex/parser/Parser.java | 4 ++-- src/main/java/decodex/parser/RecipeCommandParser.java | 2 +- .../java/decodex/{ => logic}/commands/ListCommandTest.java | 3 ++- .../java/decodex/{ => logic}/commands/SelectCommandTest.java | 3 ++- .../commands/recipe}/RecipeDeselectCommandTest.java | 4 ++-- src/test/java/decodex/parser/ParserTest.java | 4 ++-- 10 files changed, 15 insertions(+), 15 deletions(-) rename src/main/java/decodex/{ => logic}/commands/ListCommand.java (96%) rename src/main/java/decodex/{ => logic}/commands/SelectCommand.java (96%) rename src/main/java/decodex/{ => logic}/commands/recipe/RecipeDeselectCommand.java (97%) rename src/test/java/decodex/{ => logic}/commands/ListCommandTest.java (97%) rename src/test/java/decodex/{ => logic}/commands/SelectCommandTest.java (96%) rename src/test/java/decodex/{commands => logic/commands/recipe}/RecipeDeselectCommandTest.java (96%) diff --git a/src/main/java/decodex/logic/commands/HelpCommand.java b/src/main/java/decodex/logic/commands/HelpCommand.java index afb9c64e31..f0afc88258 100644 --- a/src/main/java/decodex/logic/commands/HelpCommand.java +++ b/src/main/java/decodex/logic/commands/HelpCommand.java @@ -1,10 +1,8 @@ package decodex.logic.commands; -import decodex.commands.ListCommand; -import decodex.commands.SelectCommand; import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; -import decodex.commands.recipe.RecipeDeselectCommand; +import decodex.logic.commands.recipe.RecipeDeselectCommand; import decodex.logic.commands.recipe.RecipeListCommand; import decodex.logic.commands.recipe.RecipeNewCommand; import decodex.logic.commands.recipe.RecipePopCommand; diff --git a/src/main/java/decodex/commands/ListCommand.java b/src/main/java/decodex/logic/commands/ListCommand.java similarity index 96% rename from src/main/java/decodex/commands/ListCommand.java rename to src/main/java/decodex/logic/commands/ListCommand.java index b749b2fc1d..fdb76b7336 100644 --- a/src/main/java/decodex/commands/ListCommand.java +++ b/src/main/java/decodex/logic/commands/ListCommand.java @@ -1,4 +1,4 @@ -package decodex.commands; +package decodex.logic.commands; import decodex.data.DataManager; import decodex.data.exception.CommandException; diff --git a/src/main/java/decodex/commands/SelectCommand.java b/src/main/java/decodex/logic/commands/SelectCommand.java similarity index 96% rename from src/main/java/decodex/commands/SelectCommand.java rename to src/main/java/decodex/logic/commands/SelectCommand.java index 2d3cdc67c7..81b088f9b2 100644 --- a/src/main/java/decodex/commands/SelectCommand.java +++ b/src/main/java/decodex/logic/commands/SelectCommand.java @@ -1,4 +1,4 @@ -package decodex.commands; +package decodex.logic.commands; import decodex.data.Data; import decodex.data.DataManager; diff --git a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeDeselectCommand.java similarity index 97% rename from src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java rename to src/main/java/decodex/logic/commands/recipe/RecipeDeselectCommand.java index c3fed73e5e..497e479ef2 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeselectCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeDeselectCommand.java @@ -1,4 +1,4 @@ -package decodex.commands.recipe; +package decodex.logic.commands.recipe; import java.io.IOException; diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/parser/Parser.java index a47cab33fb..2347744b92 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/parser/Parser.java @@ -6,9 +6,9 @@ import decodex.logic.commands.ExitCommand; import decodex.logic.commands.InputCommand; import decodex.logic.commands.HelpCommand; -import decodex.commands.ListCommand; +import decodex.logic.commands.ListCommand; import decodex.logic.commands.ResetCommand; -import decodex.commands.SelectCommand; +import decodex.logic.commands.SelectCommand; import decodex.logic.commands.ShowCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index 233e1c0e17..a0def5f0bf 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -2,7 +2,7 @@ import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; -import decodex.commands.recipe.RecipeDeselectCommand; +import decodex.logic.commands.recipe.RecipeDeselectCommand; import decodex.logic.commands.recipe.RecipeListCommand; import decodex.logic.commands.recipe.RecipeNewCommand; import decodex.logic.commands.recipe.RecipePopCommand; diff --git a/src/test/java/decodex/commands/ListCommandTest.java b/src/test/java/decodex/logic/commands/ListCommandTest.java similarity index 97% rename from src/test/java/decodex/commands/ListCommandTest.java rename to src/test/java/decodex/logic/commands/ListCommandTest.java index 7b3b15f7fb..c646411dcf 100644 --- a/src/test/java/decodex/commands/ListCommandTest.java +++ b/src/test/java/decodex/logic/commands/ListCommandTest.java @@ -1,8 +1,9 @@ -package decodex.commands; +package decodex.logic.commands; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.logic.commands.ListCommand; import decodex.storage.Storage; import java.io.ByteArrayOutputStream; import java.io.PrintStream; diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/logic/commands/SelectCommandTest.java similarity index 96% rename from src/test/java/decodex/commands/SelectCommandTest.java rename to src/test/java/decodex/logic/commands/SelectCommandTest.java index bb52846c2c..1086e8285d 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/logic/commands/SelectCommandTest.java @@ -1,4 +1,4 @@ -package decodex.commands; +package decodex.logic.commands; import decodex.data.Data; import decodex.data.DataManager; @@ -8,6 +8,7 @@ import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.logic.commands.SelectCommand; import decodex.modules.ModuleManager; import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexEncoder; diff --git a/src/test/java/decodex/commands/RecipeDeselectCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeDeselectCommandTest.java similarity index 96% rename from src/test/java/decodex/commands/RecipeDeselectCommandTest.java rename to src/test/java/decodex/logic/commands/recipe/RecipeDeselectCommandTest.java index d985b4f8ae..8e46a27d5e 100644 --- a/src/test/java/decodex/commands/RecipeDeselectCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeDeselectCommandTest.java @@ -1,8 +1,8 @@ -package decodex.commands; +package decodex.logic.commands.recipe; import java.io.IOException; -import decodex.commands.recipe.RecipeDeselectCommand; +import decodex.logic.commands.recipe.RecipeDeselectCommand; import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/parser/ParserTest.java index ce5304dcbf..78193dc9d5 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/parser/ParserTest.java @@ -3,9 +3,9 @@ import decodex.logic.Command; import decodex.logic.commands.ExitCommand; import decodex.logic.commands.InputCommand; -import decodex.commands.ListCommand; +import decodex.logic.commands.ListCommand; import decodex.logic.commands.ResetCommand; -import decodex.commands.SelectCommand; +import decodex.logic.commands.SelectCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; From 8cc30c3fc28c397aaf94dde837034cdfeb255bdf Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 5 Nov 2021 15:49:13 +0800 Subject: [PATCH 477/607] Moved Parser --- src/main/java/decodex/Decodex.java | 2 +- .../java/decodex/{ => logic}/parser/Parser.java | 15 ++++++++++++++- .../java/decodex/parser/RecipeCommandParser.java | 1 + .../decodex/{ => logic}/parser/ParserTest.java | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) rename src/main/java/decodex/{ => logic}/parser/Parser.java (97%) rename src/test/java/decodex/{ => logic}/parser/ParserTest.java (98%) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 71f9bb99bf..54115d12c0 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -12,7 +12,7 @@ import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; import decodex.modules.ModuleManager; -import decodex.parser.Parser; +import decodex.logic.parser.Parser; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; diff --git a/src/main/java/decodex/parser/Parser.java b/src/main/java/decodex/logic/parser/Parser.java similarity index 97% rename from src/main/java/decodex/parser/Parser.java rename to src/main/java/decodex/logic/parser/Parser.java index 2347744b92..29b293c4ed 100644 --- a/src/main/java/decodex/parser/Parser.java +++ b/src/main/java/decodex/logic/parser/Parser.java @@ -1,4 +1,4 @@ -package decodex.parser; +package decodex.logic.parser; import java.util.Arrays; @@ -12,6 +12,7 @@ import decodex.logic.commands.ShowCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; +import decodex.parser.RecipeCommandParser; import decodex.ui.messages.ErrorMessages; /** @@ -145,6 +146,7 @@ public Command parseCommand(String userInput) throws ParserException, CommandExc return command; } + // @@author arraysius /** * Splits the user input by an arbitrary number of spaces into a list of tokens. * @@ -155,7 +157,9 @@ protected String[] getTokens(String userInput) { String strippedUserInput = userInput.stripLeading(); return strippedUserInput.split(SPLIT_REGEX); } + // @@author + // @@author SeenFang /** * Prepares and returns the ExitCommand. * @@ -171,6 +175,7 @@ private ExitCommand prepareExitCommand(String userInput) throws CommandException } return new ExitCommand(); } + // @@author /** * Prepares and returns the InputCommand using the specified user input. @@ -184,6 +189,7 @@ private InputCommand prepareInputCommand(String userInput) throws ParserExceptio return new InputCommand(inputData); } + // @@author arraysius /** * Returns the list category for the list command. * @@ -202,7 +208,9 @@ private String getListCategory(String userInput) throws CommandException { throw new CommandException(ErrorMessages.TOO_MANY_COMMAND_ARGUMENTS); } } + // @@author + // @@author arraysius /** * Prepares and returns the ListCommand. * @@ -216,6 +224,7 @@ private ListCommand prepareListCommand(String userInput) throws CommandException } return new ListCommand(listCategory); } + // @@author /** * Prepares and returns the ResetCommand. @@ -226,6 +235,7 @@ private ResetCommand prepareResetCommand() { return new ResetCommand(); } + // @@author arraysius /** * Prepares and returns the SelectCommand using the specified user input. * @@ -245,6 +255,7 @@ private SelectCommand prepareSelectCommand(String userInput) throws CommandExcep return new SelectCommand(selectCategory, itemName, parameters); } + // @@author /** * Shows the original input data. @@ -261,6 +272,7 @@ private ShowCommand prepareShowCommand(String userInput) throws CommandException return new ShowCommand(); } + // @@author arraysius /** * Prepares and returns recipe subcommands using the specified user input. * @@ -271,6 +283,7 @@ private ShowCommand prepareShowCommand(String userInput) throws CommandException private Command prepareRecipeSubcommands(String userInput) throws CommandException { return recipeCommandParser.parseCommand(userInput); } + // @@author /** * Prepares and returns the HelpCommand. diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/parser/RecipeCommandParser.java index a0def5f0bf..043c92c38a 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/parser/RecipeCommandParser.java @@ -10,6 +10,7 @@ import decodex.logic.commands.recipe.RecipeResetCommand; import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.exception.CommandException; +import decodex.logic.parser.Parser; import decodex.ui.messages.ErrorMessages; import java.util.Arrays; diff --git a/src/test/java/decodex/parser/ParserTest.java b/src/test/java/decodex/logic/parser/ParserTest.java similarity index 98% rename from src/test/java/decodex/parser/ParserTest.java rename to src/test/java/decodex/logic/parser/ParserTest.java index 78193dc9d5..f3e9f8f2be 100644 --- a/src/test/java/decodex/parser/ParserTest.java +++ b/src/test/java/decodex/logic/parser/ParserTest.java @@ -1,4 +1,4 @@ -package decodex.parser; +package decodex.logic.parser; import decodex.logic.Command; import decodex.logic.commands.ExitCommand; @@ -9,6 +9,7 @@ import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; +import decodex.logic.parser.Parser; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; From 89ecf654bb985a4d709764fbe6cf37d23ea6d971 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Fri, 5 Nov 2021 15:54:45 +0800 Subject: [PATCH 478/607] Move `RecipeCommandParser` --- src/main/java/decodex/logic/parser/Dummy.txt | 0 src/main/java/decodex/logic/parser/Parser.java | 1 - .../java/decodex/{ => logic}/parser/RecipeCommandParser.java | 2 +- .../decodex/{ => logic}/parser/RecipeCommandParserTest.java | 3 ++- 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 src/main/java/decodex/logic/parser/Dummy.txt rename src/main/java/decodex/{ => logic}/parser/RecipeCommandParser.java (97%) rename src/test/java/decodex/{ => logic}/parser/RecipeCommandParserTest.java (96%) diff --git a/src/main/java/decodex/logic/parser/Dummy.txt b/src/main/java/decodex/logic/parser/Dummy.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/decodex/logic/parser/Parser.java b/src/main/java/decodex/logic/parser/Parser.java index 29b293c4ed..6441723ddb 100644 --- a/src/main/java/decodex/logic/parser/Parser.java +++ b/src/main/java/decodex/logic/parser/Parser.java @@ -12,7 +12,6 @@ import decodex.logic.commands.ShowCommand; import decodex.data.exception.CommandException; import decodex.data.exception.ParserException; -import decodex.parser.RecipeCommandParser; import decodex.ui.messages.ErrorMessages; /** diff --git a/src/main/java/decodex/parser/RecipeCommandParser.java b/src/main/java/decodex/logic/parser/RecipeCommandParser.java similarity index 97% rename from src/main/java/decodex/parser/RecipeCommandParser.java rename to src/main/java/decodex/logic/parser/RecipeCommandParser.java index 043c92c38a..7368f4c3aa 100644 --- a/src/main/java/decodex/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/logic/parser/RecipeCommandParser.java @@ -1,4 +1,4 @@ -package decodex.parser; +package decodex.logic.parser; import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; diff --git a/src/test/java/decodex/parser/RecipeCommandParserTest.java b/src/test/java/decodex/logic/parser/RecipeCommandParserTest.java similarity index 96% rename from src/test/java/decodex/parser/RecipeCommandParserTest.java rename to src/test/java/decodex/logic/parser/RecipeCommandParserTest.java index 02ee8674fb..ed3a390f1b 100644 --- a/src/test/java/decodex/parser/RecipeCommandParserTest.java +++ b/src/test/java/decodex/logic/parser/RecipeCommandParserTest.java @@ -1,4 +1,4 @@ -package decodex.parser; +package decodex.logic.parser; import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; @@ -8,6 +8,7 @@ import decodex.logic.commands.recipe.RecipeResetCommand; import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.exception.CommandException; +import decodex.logic.parser.RecipeCommandParser; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 9772cf3ab2543f1cec4518114d84c35f53cfcc93 Mon Sep 17 00:00:00 2001 From: rizemon Date: Fri, 5 Nov 2021 16:06:04 +0800 Subject: [PATCH 479/607] Fix import order --- src/main/java/decodex/data/DataManager.java | 3 ++- .../java/decodex/logic/commands/HelpCommand.java | 2 +- .../logic/commands/recipe/RecipePopCommand.java | 5 +++-- .../logic/commands/recipe/RecipePushCommand.java | 7 ++++--- .../logic/commands/recipe/RecipeResetCommand.java | 5 +++-- .../commands/recipe/RecipeSelectCommand.java | 2 +- src/test/java/decodex/data/DataManagerTest.java | 6 +++--- src/test/java/decodex/data/DataTest.java | 5 ++--- .../decodex/logic/commands/HelpCommandTest.java | 14 ++++++-------- .../decodex/logic/commands/InputCommandTest.java | 12 +++++------- .../decodex/logic/commands/ResetCommandTest.java | 11 ++++------- .../commands/recipe/RecipePopCommandTest.java | 10 +++++----- .../commands/recipe/RecipePushCommandTest.java | 8 ++++---- .../commands/recipe/RecipeResetCommandTest.java | 15 +++++++-------- .../commands/recipe/RecipeSelectCommandTest.java | 15 +++++++-------- 15 files changed, 57 insertions(+), 63 deletions(-) diff --git a/src/main/java/decodex/data/DataManager.java b/src/main/java/decodex/data/DataManager.java index 28157216d0..b559257de2 100644 --- a/src/main/java/decodex/data/DataManager.java +++ b/src/main/java/decodex/data/DataManager.java @@ -1,8 +1,9 @@ package decodex.data; +import java.util.Arrays; + import decodex.data.exception.DataManagerException; import decodex.ui.messages.ErrorMessages; -import java.util.Arrays; /** * The DataManager class handles the management of the current state of the data. diff --git a/src/main/java/decodex/logic/commands/HelpCommand.java b/src/main/java/decodex/logic/commands/HelpCommand.java index f0afc88258..3353df9fb1 100644 --- a/src/main/java/decodex/logic/commands/HelpCommand.java +++ b/src/main/java/decodex/logic/commands/HelpCommand.java @@ -1,5 +1,6 @@ package decodex.logic.commands; +import decodex.data.DataManager; import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.logic.commands.recipe.RecipeDeselectCommand; @@ -9,7 +10,6 @@ import decodex.logic.commands.recipe.RecipePushCommand; import decodex.logic.commands.recipe.RecipeResetCommand; import decodex.logic.commands.recipe.RecipeSelectCommand; -import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; diff --git a/src/main/java/decodex/logic/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipePopCommand.java index 36d9c42aae..7a64910579 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipePopCommand.java @@ -1,17 +1,18 @@ package decodex.logic.commands.recipe; -import decodex.logic.Command; +import java.io.IOException; + import decodex.data.DataManager; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; +import decodex.logic.Command; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; // @@author rizemon public class RecipePopCommand extends Command { diff --git a/src/main/java/decodex/logic/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipePushCommand.java index 0fb69d9f8c..24892fa47b 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipePushCommand.java @@ -1,12 +1,14 @@ package decodex.logic.commands.recipe; -import decodex.logic.Command; +import java.io.IOException; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeManagerException; import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; +import decodex.logic.Command; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; @@ -14,7 +16,6 @@ import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; -import java.io.IOException; // @@author rizemon public class RecipePushCommand extends Command { diff --git a/src/main/java/decodex/logic/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeResetCommand.java index 4f2d13738e..702d20e4d3 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeResetCommand.java @@ -1,15 +1,16 @@ package decodex.logic.commands.recipe; -import decodex.logic.Command; +import java.io.IOException; + import decodex.data.DataManager; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; // @@author rizemon public class RecipeResetCommand extends Command { diff --git a/src/main/java/decodex/logic/commands/recipe/RecipeSelectCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeSelectCommand.java index 8e3352ca84..01efd222f2 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipeSelectCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeSelectCommand.java @@ -1,9 +1,9 @@ package decodex.logic.commands.recipe; -import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.RecipeManagerException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; diff --git a/src/test/java/decodex/data/DataManagerTest.java b/src/test/java/decodex/data/DataManagerTest.java index 1ba60148e4..dab6b3240c 100644 --- a/src/test/java/decodex/data/DataManagerTest.java +++ b/src/test/java/decodex/data/DataManagerTest.java @@ -1,12 +1,12 @@ package decodex.data; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; import decodex.data.exception.DataManagerException; -import java.util.Arrays; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class DataManagerTest { diff --git a/src/test/java/decodex/data/DataTest.java b/src/test/java/decodex/data/DataTest.java index e485bbdf43..361204559f 100644 --- a/src/test/java/decodex/data/DataTest.java +++ b/src/test/java/decodex/data/DataTest.java @@ -1,10 +1,9 @@ package decodex.data; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; import org.junit.jupiter.api.Test; - -import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertTrue; class DataTest { diff --git a/src/test/java/decodex/logic/commands/HelpCommandTest.java b/src/test/java/decodex/logic/commands/HelpCommandTest.java index 02808b5ad1..1fc2e79841 100644 --- a/src/test/java/decodex/logic/commands/HelpCommandTest.java +++ b/src/test/java/decodex/logic/commands/HelpCommandTest.java @@ -1,21 +1,19 @@ package decodex.logic.commands; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import decodex.logic.commands.HelpCommand; -import decodex.storage.Storage; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import java.io.ByteArrayOutputStream; import java.io.PrintStream; import decodex.data.DataManager; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; + // @@author rizemon class HelpCommandTest { diff --git a/src/test/java/decodex/logic/commands/InputCommandTest.java b/src/test/java/decodex/logic/commands/InputCommandTest.java index cda9cfdb39..02d8e520f4 100644 --- a/src/test/java/decodex/logic/commands/InputCommandTest.java +++ b/src/test/java/decodex/logic/commands/InputCommandTest.java @@ -1,12 +1,5 @@ package decodex.logic.commands; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import decodex.logic.commands.InputCommand; -import decodex.storage.Storage; -import org.junit.jupiter.api.Test; - import java.util.Arrays; import decodex.data.Data; @@ -15,8 +8,13 @@ import decodex.data.exception.DataManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + // @@author rizemon public class InputCommandTest { diff --git a/src/test/java/decodex/logic/commands/ResetCommandTest.java b/src/test/java/decodex/logic/commands/ResetCommandTest.java index d364df51e3..d1009d42b4 100644 --- a/src/test/java/decodex/logic/commands/ResetCommandTest.java +++ b/src/test/java/decodex/logic/commands/ResetCommandTest.java @@ -1,11 +1,5 @@ package decodex.logic.commands; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import decodex.logic.commands.ResetCommand; -import decodex.storage.Storage; -import org.junit.jupiter.api.Test; - import java.util.Arrays; import decodex.data.Data; @@ -13,9 +7,12 @@ import decodex.data.exception.DataManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; - +import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; + // @@author rizemon class ResetCommandTest { diff --git a/src/test/java/decodex/logic/commands/recipe/RecipePopCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipePopCommandTest.java index 1bc4e0febe..9bd9921a88 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipePopCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipePopCommandTest.java @@ -1,9 +1,7 @@ package decodex.logic.commands.recipe; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.IOException; -import decodex.logic.commands.recipe.RecipePopCommand; import decodex.data.DataManager; import decodex.data.exception.ModuleException; import decodex.data.exception.ModuleManagerException; @@ -15,8 +13,10 @@ import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; + import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; // @@author rizemon class RecipePopCommandTest { @@ -46,7 +46,7 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsZero() } @Test - public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException, IOException { + public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); RecipeManager recipeManager = new RecipeManager(); diff --git a/src/test/java/decodex/logic/commands/recipe/RecipePushCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipePushCommandTest.java index ed462a923e..18cdaa819c 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipePushCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipePushCommandTest.java @@ -1,9 +1,7 @@ package decodex.logic.commands.recipe; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.IOException; -import decodex.logic.commands.recipe.RecipePushCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; @@ -15,8 +13,10 @@ import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; + import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; // @@author rizemon class RecipePushCommandTest { diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeResetCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeResetCommandTest.java index ed531314b1..b319943246 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeResetCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeResetCommandTest.java @@ -1,23 +1,22 @@ package decodex.logic.commands.recipe; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import decodex.data.exception.RecipeException; -import decodex.storage.Storage; import java.io.IOException; -import org.junit.jupiter.api.Test; -import decodex.logic.commands.recipe.RecipeResetCommand; import decodex.data.DataManager; import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeManagerException; import decodex.data.exception.ModuleManagerException; -import decodex.modules.ModuleManager; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; import decodex.modules.Module; +import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + // @@author rizemon class RecipeResetCommandTest { diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeSelectCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeSelectCommandTest.java index 47cdd340bf..8ae5a5691c 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeSelectCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeSelectCommandTest.java @@ -1,28 +1,27 @@ package decodex.logic.commands.recipe; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import decodex.data.exception.RecipeException; -import decodex.storage.Storage; import java.io.IOException; -import org.junit.jupiter.api.Test; -import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; +import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + // @@author rizemon class RecipeSelectCommandTest { @Test public void run_selectExistingRecipe_existingRecipe() throws RecipeException, RecipeManagerException, - CommandException, IOException { + CommandException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Storage storage = new Storage(); From 2ab0012f22edaa69c28411d2364f1401b8430876 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Fri, 5 Nov 2021 16:08:45 +0800 Subject: [PATCH 480/607] Reorder imports --- .../logic/commands/recipe/RecipeDeselectCommand.java | 2 +- .../java/decodex/logic/parser/RecipeCommandParser.java | 6 +++--- src/main/java/decodex/modules/Module.java | 1 + src/main/java/decodex/modules/binary/BinaryDecoder.java | 3 ++- src/test/java/decodex/logic/commands/ListCommandTest.java | 8 +++----- .../java/decodex/logic/commands/SelectCommandTest.java | 4 ++-- .../logic/commands/recipe/RecipeDeselectCommandTest.java | 2 -- .../decodex/logic/parser/RecipeCommandParserTest.java | 3 +-- src/test/java/decodex/modules/ModuleManagerTest.java | 5 ++--- .../java/decodex/modules/binary/BinaryDecoderTest.java | 6 +++--- .../java/decodex/modules/binary/BinaryEncoderTest.java | 4 ++-- 11 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/main/java/decodex/logic/commands/recipe/RecipeDeselectCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeDeselectCommand.java index 497e479ef2..2b0504177b 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipeDeselectCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeDeselectCommand.java @@ -2,7 +2,6 @@ import java.io.IOException; -import decodex.logic.Command; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; @@ -10,6 +9,7 @@ import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; diff --git a/src/main/java/decodex/logic/parser/RecipeCommandParser.java b/src/main/java/decodex/logic/parser/RecipeCommandParser.java index 7368f4c3aa..08339b91ed 100644 --- a/src/main/java/decodex/logic/parser/RecipeCommandParser.java +++ b/src/main/java/decodex/logic/parser/RecipeCommandParser.java @@ -1,5 +1,8 @@ package decodex.logic.parser; +import java.util.Arrays; + +import decodex.data.exception.CommandException; import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.logic.commands.recipe.RecipeDeselectCommand; @@ -9,10 +12,7 @@ import decodex.logic.commands.recipe.RecipePushCommand; import decodex.logic.commands.recipe.RecipeResetCommand; import decodex.logic.commands.recipe.RecipeSelectCommand; -import decodex.data.exception.CommandException; -import decodex.logic.parser.Parser; import decodex.ui.messages.ErrorMessages; -import java.util.Arrays; /** * The RecipeCommandParser class handles the parsing and validation of recipe commands. diff --git a/src/main/java/decodex/modules/Module.java b/src/main/java/decodex/modules/Module.java index 6a0ab27cdb..e43acdd830 100644 --- a/src/main/java/decodex/modules/Module.java +++ b/src/main/java/decodex/modules/Module.java @@ -39,6 +39,7 @@ public String toString() { * Performs data processing on the provided Data object. Abstract method to be implemented by other modules. * * @return Data object containing new processed data. + * @throws ModuleException If module execution failed. */ public abstract Data run(Data data) throws ModuleException; } diff --git a/src/main/java/decodex/modules/binary/BinaryDecoder.java b/src/main/java/decodex/modules/binary/BinaryDecoder.java index 906a7a7e5a..f1d2972aba 100644 --- a/src/main/java/decodex/modules/binary/BinaryDecoder.java +++ b/src/main/java/decodex/modules/binary/BinaryDecoder.java @@ -1,10 +1,11 @@ package decodex.modules.binary; -import decodex.modules.Module; import java.util.regex.Matcher; import java.util.regex.Pattern; + import decodex.data.Data; import decodex.data.exception.ModuleException; +import decodex.modules.Module; import decodex.ui.messages.ErrorMessages; // @@author arraysius diff --git a/src/test/java/decodex/logic/commands/ListCommandTest.java b/src/test/java/decodex/logic/commands/ListCommandTest.java index c646411dcf..1e6a07b036 100644 --- a/src/test/java/decodex/logic/commands/ListCommandTest.java +++ b/src/test/java/decodex/logic/commands/ListCommandTest.java @@ -1,10 +1,5 @@ package decodex.logic.commands; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import decodex.logic.commands.ListCommand; -import decodex.storage.Storage; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -12,11 +7,14 @@ import decodex.data.exception.CommandException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; public class ListCommandTest { diff --git a/src/test/java/decodex/logic/commands/SelectCommandTest.java b/src/test/java/decodex/logic/commands/SelectCommandTest.java index 1086e8285d..1b79f4d36b 100644 --- a/src/test/java/decodex/logic/commands/SelectCommandTest.java +++ b/src/test/java/decodex/logic/commands/SelectCommandTest.java @@ -1,5 +1,7 @@ package decodex.logic.commands; +import java.io.IOException; + import decodex.data.Data; import decodex.data.DataManager; import decodex.data.exception.CommandException; @@ -8,7 +10,6 @@ import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.logic.commands.SelectCommand; import decodex.modules.ModuleManager; import decodex.modules.base64.Base64Encoder; import decodex.modules.hex.HexEncoder; @@ -17,7 +18,6 @@ import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeDeselectCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeDeselectCommandTest.java index 8e46a27d5e..ede52f9c83 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeDeselectCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeDeselectCommandTest.java @@ -2,8 +2,6 @@ import java.io.IOException; -import decodex.logic.commands.recipe.RecipeDeselectCommand; -import decodex.logic.commands.recipe.RecipeSelectCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; diff --git a/src/test/java/decodex/logic/parser/RecipeCommandParserTest.java b/src/test/java/decodex/logic/parser/RecipeCommandParserTest.java index ed3a390f1b..d359c61cbe 100644 --- a/src/test/java/decodex/logic/parser/RecipeCommandParserTest.java +++ b/src/test/java/decodex/logic/parser/RecipeCommandParserTest.java @@ -1,5 +1,6 @@ package decodex.logic.parser; +import decodex.data.exception.CommandException; import decodex.logic.Command; import decodex.logic.commands.recipe.RecipeDeleteCommand; import decodex.logic.commands.recipe.RecipeNewCommand; @@ -7,8 +8,6 @@ import decodex.logic.commands.recipe.RecipePushCommand; import decodex.logic.commands.recipe.RecipeResetCommand; import decodex.logic.commands.recipe.RecipeSelectCommand; -import decodex.data.exception.CommandException; -import decodex.logic.parser.RecipeCommandParser; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/decodex/modules/ModuleManagerTest.java b/src/test/java/decodex/modules/ModuleManagerTest.java index 2b37dd4923..30e1c62a9a 100644 --- a/src/test/java/decodex/modules/ModuleManagerTest.java +++ b/src/test/java/decodex/modules/ModuleManagerTest.java @@ -1,14 +1,13 @@ package decodex.modules; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import decodex.data.exception.ModuleException; import decodex.data.exception.ModuleManagerException; import decodex.modules.base64.Base64Encoder; import decodex.modules.rot.RotEncoder; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ModuleManagerTest { diff --git a/src/test/java/decodex/modules/binary/BinaryDecoderTest.java b/src/test/java/decodex/modules/binary/BinaryDecoderTest.java index af3f22c1f1..fb5e5a6e9c 100644 --- a/src/test/java/decodex/modules/binary/BinaryDecoderTest.java +++ b/src/test/java/decodex/modules/binary/BinaryDecoderTest.java @@ -1,11 +1,11 @@ package decodex.modules.binary; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import decodex.data.Data; import decodex.data.exception.ModuleException; + import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class BinaryDecoderTest { diff --git a/src/test/java/decodex/modules/binary/BinaryEncoderTest.java b/src/test/java/decodex/modules/binary/BinaryEncoderTest.java index c39a4a5371..638f88a96d 100644 --- a/src/test/java/decodex/modules/binary/BinaryEncoderTest.java +++ b/src/test/java/decodex/modules/binary/BinaryEncoderTest.java @@ -1,9 +1,9 @@ package decodex.modules.binary; -import static org.junit.jupiter.api.Assertions.assertEquals; - import decodex.data.Data; + import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BinaryEncoderTest { From 8bc174aeb22385a77162bfde7040c93bf9d92586 Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Fri, 5 Nov 2021 16:11:47 +0800 Subject: [PATCH 481/607] Modified import orders. --- src/main/java/decodex/Decodex.java | 14 ++++++------- .../java/decodex/logic/parser/Parser.java | 6 +++--- .../decodex/modules/base64/Base64Decoder.java | 3 ++- .../decodex/modules/base64/Base64Encoder.java | 4 ++-- src/main/java/decodex/storage/Storage.java | 20 +++++++++---------- src/test/java/decodex/DecodexTest.java | 3 +-- .../java/decodex/logic/parser/ParserTest.java | 5 ++--- .../modules/base64/Base64DecoderTest.java | 2 +- .../modules/base64/Base64EncoderTest.java | 2 +- 9 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 54115d12c0..ff2f922796 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -1,7 +1,9 @@ package decodex; -import decodex.logic.Command; -import decodex.logic.commands.ExitCommand; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; @@ -11,16 +13,14 @@ import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; -import decodex.modules.ModuleManager; +import decodex.logic.Command; +import decodex.logic.commands.ExitCommand; import decodex.logic.parser.Parser; +import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - public class Decodex { public static Logger logger = Logger.getLogger(Decodex.class.getName()); diff --git a/src/main/java/decodex/logic/parser/Parser.java b/src/main/java/decodex/logic/parser/Parser.java index 6441723ddb..1af5b7f131 100644 --- a/src/main/java/decodex/logic/parser/Parser.java +++ b/src/main/java/decodex/logic/parser/Parser.java @@ -2,16 +2,16 @@ import java.util.Arrays; +import decodex.data.exception.CommandException; +import decodex.data.exception.ParserException; import decodex.logic.Command; import decodex.logic.commands.ExitCommand; -import decodex.logic.commands.InputCommand; import decodex.logic.commands.HelpCommand; +import decodex.logic.commands.InputCommand; import decodex.logic.commands.ListCommand; import decodex.logic.commands.ResetCommand; import decodex.logic.commands.SelectCommand; import decodex.logic.commands.ShowCommand; -import decodex.data.exception.CommandException; -import decodex.data.exception.ParserException; import decodex.ui.messages.ErrorMessages; /** diff --git a/src/main/java/decodex/modules/base64/Base64Decoder.java b/src/main/java/decodex/modules/base64/Base64Decoder.java index 16d2e97b95..afe29595fd 100644 --- a/src/main/java/decodex/modules/base64/Base64Decoder.java +++ b/src/main/java/decodex/modules/base64/Base64Decoder.java @@ -1,10 +1,11 @@ package decodex.modules.base64; +import java.util.Base64; + import decodex.data.Data; import decodex.data.exception.ModuleException; import decodex.modules.Module; import decodex.ui.messages.ErrorMessages; -import java.util.Base64; // @@author Kair0s3 /** diff --git a/src/main/java/decodex/modules/base64/Base64Encoder.java b/src/main/java/decodex/modules/base64/Base64Encoder.java index eaff65a6d5..1bd7e0ef0b 100644 --- a/src/main/java/decodex/modules/base64/Base64Encoder.java +++ b/src/main/java/decodex/modules/base64/Base64Encoder.java @@ -1,10 +1,10 @@ package decodex.modules.base64; +import java.util.Base64; + import decodex.data.Data; import decodex.modules.Module; -import java.util.Base64; - // @@author Kair0s3 /** * Base64Encoder serves to carry out the Base64 encoding operations. diff --git a/src/main/java/decodex/storage/Storage.java b/src/main/java/decodex/storage/Storage.java index a3dc0b8762..c1be369126 100644 --- a/src/main/java/decodex/storage/Storage.java +++ b/src/main/java/decodex/storage/Storage.java @@ -1,26 +1,26 @@ package decodex.storage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; + import decodex.data.exception.ModuleException; import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; -import decodex.modules.ModuleManager; import decodex.modules.Module; +import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; - // @@author Kair0s3 public class Storage { diff --git a/src/test/java/decodex/DecodexTest.java b/src/test/java/decodex/DecodexTest.java index 4dc5b378c5..0b4ac5d675 100644 --- a/src/test/java/decodex/DecodexTest.java +++ b/src/test/java/decodex/DecodexTest.java @@ -1,8 +1,7 @@ package decodex; -import static org.junit.jupiter.api.Assertions.assertTrue; - import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; class DecodexTest { diff --git a/src/test/java/decodex/logic/parser/ParserTest.java b/src/test/java/decodex/logic/parser/ParserTest.java index f3e9f8f2be..e20d922e57 100644 --- a/src/test/java/decodex/logic/parser/ParserTest.java +++ b/src/test/java/decodex/logic/parser/ParserTest.java @@ -1,15 +1,14 @@ package decodex.logic.parser; +import decodex.data.exception.CommandException; +import decodex.data.exception.ParserException; import decodex.logic.Command; import decodex.logic.commands.ExitCommand; import decodex.logic.commands.InputCommand; import decodex.logic.commands.ListCommand; import decodex.logic.commands.ResetCommand; import decodex.logic.commands.SelectCommand; -import decodex.data.exception.CommandException; -import decodex.data.exception.ParserException; -import decodex.logic.parser.Parser; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/src/test/java/decodex/modules/base64/Base64DecoderTest.java b/src/test/java/decodex/modules/base64/Base64DecoderTest.java index e6852745fe..0bed1df673 100644 --- a/src/test/java/decodex/modules/base64/Base64DecoderTest.java +++ b/src/test/java/decodex/modules/base64/Base64DecoderTest.java @@ -2,8 +2,8 @@ import decodex.data.Data; import decodex.data.exception.ModuleException; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/src/test/java/decodex/modules/base64/Base64EncoderTest.java b/src/test/java/decodex/modules/base64/Base64EncoderTest.java index d8fdc62f32..3f1503a53a 100644 --- a/src/test/java/decodex/modules/base64/Base64EncoderTest.java +++ b/src/test/java/decodex/modules/base64/Base64EncoderTest.java @@ -1,8 +1,8 @@ package decodex.modules.base64; import decodex.data.Data; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class Base64EncoderTest { From cfe171f669239abcc9dec508e71624358617d1bf Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Fri, 5 Nov 2021 16:13:26 +0800 Subject: [PATCH 482/607] Reorder Imports --- src/main/java/decodex/logic/Command.java | 7 +++++-- .../commands/recipe/RecipeDeleteCommand.java | 6 ++++-- .../commands/recipe/RecipeListCommand.java | 6 ++++-- .../commands/recipe/RecipeNewCommand.java | 6 ++++-- .../java/decodex/modules/hex/HexDecoder.java | 2 ++ .../java/decodex/modules/hex/HexEncoder.java | 2 ++ src/main/java/decodex/recipes/Recipe.java | 4 +++- .../recipe/RecipeDeleteCommandTest.java | 9 +++++---- .../recipe/RecipeListCommandTest.java | 19 ++++++++++--------- .../commands/recipe/RecipeNewCommandTest.java | 9 +++++---- .../decodex/recipes/RecipeManagerTest.java | 8 ++++---- src/test/java/decodex/recipes/RecipeTest.java | 4 +++- 12 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/main/java/decodex/logic/Command.java b/src/main/java/decodex/logic/Command.java index 34cadf62e6..b3f90d3f6a 100644 --- a/src/main/java/decodex/logic/Command.java +++ b/src/main/java/decodex/logic/Command.java @@ -1,17 +1,20 @@ package decodex.logic; +import java.io.IOException; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; + +// @@author SeenFang /** * Command serves as a foundation for other commands to be built upon. diff --git a/src/main/java/decodex/logic/commands/recipe/RecipeDeleteCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeDeleteCommand.java index 567d25770d..d489cd9e74 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipeDeleteCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeDeleteCommand.java @@ -1,18 +1,20 @@ package decodex.logic.commands.recipe; -import decodex.logic.Command; +import java.io.IOException; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; -import java.io.IOException; +// @@author SeenFang public class RecipeDeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; diff --git a/src/main/java/decodex/logic/commands/recipe/RecipeListCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeListCommand.java index d40816e285..7cce3fbfa1 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipeListCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeListCommand.java @@ -1,10 +1,12 @@ package decodex.logic.commands.recipe; -import decodex.logic.Command; +import java.util.ArrayList; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeManagerException; +import decodex.logic.Command; import decodex.modules.Module; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; @@ -12,8 +14,8 @@ import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.RegularMessages; -import java.util.ArrayList; +// @@author SeenFang public class RecipeListCommand extends Command { public static final String COMMAND_WORD = "list"; diff --git a/src/main/java/decodex/logic/commands/recipe/RecipeNewCommand.java b/src/main/java/decodex/logic/commands/recipe/RecipeNewCommand.java index 0a973840fb..da06707760 100644 --- a/src/main/java/decodex/logic/commands/recipe/RecipeNewCommand.java +++ b/src/main/java/decodex/logic/commands/recipe/RecipeNewCommand.java @@ -1,20 +1,22 @@ package decodex.logic.commands.recipe; -import decodex.logic.Command; +import java.io.IOException; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.data.exception.StorageException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; -import java.io.IOException; +// @@author SeenFang public class RecipeNewCommand extends Command { public static final String COMMAND_WORD = "new"; diff --git a/src/main/java/decodex/modules/hex/HexDecoder.java b/src/main/java/decodex/modules/hex/HexDecoder.java index abcc99bfb0..85ca22c103 100644 --- a/src/main/java/decodex/modules/hex/HexDecoder.java +++ b/src/main/java/decodex/modules/hex/HexDecoder.java @@ -5,6 +5,8 @@ import decodex.modules.Module; import decodex.ui.messages.ErrorMessages; +// @@author SeenFang + /** * The HexDecoder class converts a hexadecimal string back into its raw value. */ diff --git a/src/main/java/decodex/modules/hex/HexEncoder.java b/src/main/java/decodex/modules/hex/HexEncoder.java index fea219aba8..2e0a6b8c33 100644 --- a/src/main/java/decodex/modules/hex/HexEncoder.java +++ b/src/main/java/decodex/modules/hex/HexEncoder.java @@ -3,6 +3,8 @@ import decodex.data.Data; import decodex.modules.Module; +// @@author SeenFang + /** * The HexEncoder class converts the input string to hexadecimal bytes. */ diff --git a/src/main/java/decodex/recipes/Recipe.java b/src/main/java/decodex/recipes/Recipe.java index 71d6c76bcf..cb3269d614 100644 --- a/src/main/java/decodex/recipes/Recipe.java +++ b/src/main/java/decodex/recipes/Recipe.java @@ -5,12 +5,14 @@ import java.util.regex.Pattern; import decodex.Decodex; -import decodex.data.exception.RecipeException; import decodex.data.Data; import decodex.data.exception.ModuleException; +import decodex.data.exception.RecipeException; import decodex.modules.Module; import decodex.ui.messages.ErrorMessages; +// @@author SeenFang + /** * The Recipe class manages a list of Modules to be run sequentially. */ diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java index 071dbc26ef..bba5ff552d 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java @@ -2,8 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.logic.Command; -import decodex.logic.commands.recipe.RecipeDeleteCommand; +import java.io.IOException; +import org.junit.jupiter.api.Test; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; @@ -11,14 +12,14 @@ import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; -import org.junit.jupiter.api.Test; +// @@author SeenFang class RecipeDeleteCommandTest { @Test diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java index e2782638b5..e7f87239e2 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java @@ -3,7 +3,13 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.logic.commands.recipe.RecipeListCommand; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; @@ -18,13 +24,8 @@ import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +// @@author SeenFang class RecipeListCommandTest { private static final String TEST_RECIPE_NAME = "testRecipe"; @@ -72,7 +73,7 @@ void run_listRecipeName_success() throws RecipeManagerException, CommandExceptio void run_listBlankNoEditingRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand(""); assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } @Test @@ -87,7 +88,7 @@ void run_listBlankHasEditingRecipe_success() throws RecipeManagerException, Comm void run_listUnknownRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand("unknownRecipe"); assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } @Test diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java index bfc1b7fb4d..be8bd640d4 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java @@ -3,8 +3,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.logic.Command; -import decodex.logic.commands.recipe.RecipeNewCommand; +import java.io.IOException; +import org.junit.jupiter.api.Test; + import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; @@ -12,13 +13,13 @@ import decodex.data.exception.ModuleManagerException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; +import decodex.logic.Command; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; -import java.io.IOException; -import org.junit.jupiter.api.Test; +// @@author SeenFang class RecipeNewCommandTest { @Test diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index 0622ca2c07..48bd95bc93 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -3,14 +3,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.Module; import decodex.modules.hex.HexEncoder; -import decodex.storage.Storage; -import java.io.IOException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; // @@author rizemon class RecipeManagerTest { diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index 5030b4eff3..e9358e99b4 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -4,19 +4,21 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.Test; import decodex.data.Data; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; +import decodex.modules.Module; import decodex.modules.base64.Base64Decoder; import decodex.modules.base64.Base64Encoder; import decodex.modules.binary.BinaryEncoder; import decodex.modules.hex.HexDecoder; import decodex.modules.hex.HexEncoder; -import decodex.modules.Module; import decodex.modules.rot.RotEncoder; +// @@author SeenFang class RecipeTest { @Test From 989a9cb54fd9ce1019707ef452e1673bbbb63856 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Fri, 5 Nov 2021 16:15:50 +0800 Subject: [PATCH 483/607] Fix indents --- .../decodex/logic/commands/recipe/RecipeListCommandTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java index e7f87239e2..dcdfa470d0 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java @@ -73,7 +73,7 @@ void run_listRecipeName_success() throws RecipeManagerException, CommandExceptio void run_listBlankNoEditingRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand(""); assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } @Test @@ -88,7 +88,7 @@ void run_listBlankHasEditingRecipe_success() throws RecipeManagerException, Comm void run_listUnknownRecipe_expectException() { RecipeListCommand testCommand = new RecipeListCommand("unknownRecipe"); assertThrows(RecipeManagerException.class, - () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); + () -> testCommand.run(dataManager, moduleManager, ui, recipeManager, storage)); } @Test From e19fe1a1208ad218a55d5a869d6681d00f27f9bb Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Fri, 5 Nov 2021 16:23:55 +0800 Subject: [PATCH 484/607] Reorder imports 2 --- .../commands/recipe/RecipeDeleteCommandTest.java | 6 +++--- .../commands/recipe/RecipeListCommandTest.java | 12 ++++++------ .../logic/commands/recipe/RecipeNewCommandTest.java | 8 ++++---- .../java/decodex/recipes/RecipeManagerTest.java | 10 +++++----- src/test/java/decodex/recipes/RecipeTest.java | 13 ++++++------- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java index bba5ff552d..ce131c0906 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeDeleteCommandTest.java @@ -1,9 +1,6 @@ package decodex.logic.commands.recipe; -import static org.junit.jupiter.api.Assertions.assertThrows; - import java.io.IOException; -import org.junit.jupiter.api.Test; import decodex.data.DataManager; import decodex.data.exception.CommandException; @@ -19,6 +16,9 @@ import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; + // @@author SeenFang class RecipeDeleteCommandTest { diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java index dcdfa470d0..c91322350f 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeListCommandTest.java @@ -1,14 +1,8 @@ package decodex.logic.commands.recipe; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import decodex.data.DataManager; import decodex.data.exception.CommandException; @@ -25,6 +19,12 @@ import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + // @@author SeenFang class RecipeListCommandTest { diff --git a/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java b/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java index be8bd640d4..056e813276 100644 --- a/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java +++ b/src/test/java/decodex/logic/commands/recipe/RecipeNewCommandTest.java @@ -1,10 +1,6 @@ package decodex.logic.commands.recipe; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - import java.io.IOException; -import org.junit.jupiter.api.Test; import decodex.data.DataManager; import decodex.data.exception.CommandException; @@ -19,6 +15,10 @@ import decodex.storage.Storage; import decodex.ui.Ui; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + // @@author SeenFang class RecipeNewCommandTest { diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index 48bd95bc93..aa0c7db78c 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -1,17 +1,17 @@ package decodex.recipes; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import java.io.IOException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; import decodex.modules.Module; import decodex.modules.hex.HexEncoder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + // @@author rizemon class RecipeManagerTest { diff --git a/src/test/java/decodex/recipes/RecipeTest.java b/src/test/java/decodex/recipes/RecipeTest.java index e9358e99b4..0377685b7c 100644 --- a/src/test/java/decodex/recipes/RecipeTest.java +++ b/src/test/java/decodex/recipes/RecipeTest.java @@ -1,12 +1,5 @@ package decodex.recipes; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - import decodex.data.Data; import decodex.data.exception.ModuleException; import decodex.data.exception.RecipeException; @@ -18,6 +11,12 @@ import decodex.modules.hex.HexEncoder; import decodex.modules.rot.RotEncoder; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + // @@author SeenFang class RecipeTest { From 9ba3dfc0e4296729654722d6505cd7c17d80b3da Mon Sep 17 00:00:00 2001 From: Kair0s3 Date: Sat, 6 Nov 2021 00:49:30 +0800 Subject: [PATCH 485/607] Added link for the available modules keyword, quick start changes, and storage doc added. --- docs/UserGuide.md | 49 +++++++++++++++++-------- docs/images/quickStartPrompt.png | Bin 0 -> 18496 bytes docs/images/quickStartPromptEdited.png | Bin 0 -> 506216 bytes docs/team/kair0s3.md | 2 +- 4 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 docs/images/quickStartPrompt.png create mode 100644 docs/images/quickStartPromptEdited.png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index f6fbbc27f5..a55d53a798 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -33,7 +33,11 @@ This guide serves to help you understand the usage of the program to encode and - [Remove a Module from a Recipe: `recipe pop`](#remove-a-module-from-a-recipe-recipe-pop) - [Clear All Modules in a Recipe: `recipe reset`](#clear-all-modules-in-a-recipe-recipe-reset) - [Delete a Recipe: `recipe delete`](#delete-a-recipe-recipe-delete) - - [Saving Recipe to File](#saving-recipe-to-file) + - [Storage of Recipe Files](#storage-of-recipe-files) + - [Load Recipe Files on Startup](#load-recipe-files-on-startup) + - [Save Recipe to File](#save-recipe-to-file) + - [Delete Recipe File](#delete-recipe-file) + - [Reading/Writing Data from/to File `coming soon in the future`](#readingwriting-data-fromto-file-coming-soon-in-the-future) - [Command Summary](#command-summary) - [FAQ](#faq) @@ -49,7 +53,7 @@ This section serves to help you better understand the terminologies used in this | Base64, Binary, Hexadecimal | Common types of data encoding standards. | | Console | This refers to your command prompt window. | | Argument | The additional information you provide to the program's command. | -| Module | A self-contained set of instructions to process your data into another form. | +| Module :bulb: To open command prompt, press `win + r` at the same time, then type and enter `cmd`. 4. Afterwards, you can run `decodex.jar` by typing in `java -jar decodex.jar` and Decodex's prompt should appear. Please also ensure that you are in the same directory as where you have downloaded `decodex.jar`. - 1. In the screenshot below, `decodex.jar` is located in the `Downloads` folder. - ![carbon(23).png](images/carbon(23).png) -5. You can try out some of the basic commands below: + 1. In the screenshot below, you can see that `decodex.jar` is run in the `Downloads` folder. To add on, it also shows you how to better understand the startup prompt of Decodex. + ![quickStartPrompt](images/quickStartPromptEdited.png) +5. Now, you are ready to try out some of the basic commands below: 1. `input I am groot`: Inputs the text data `I am groot` into the program. 2. `list`: Lists all available modules and recipes that you can use. 3. `select module base64encode`: Selects and runs the base64-encoding module on the data. 4. `reset`: Resets the changes made to data - resetting to its original data. 5. `exit`: Exits the application. - > :video_game: Side-Quest! You may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` - > using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! + > :video_game: To make things more interesting while learning how to use Decodex, you may try to find out what `NTEgMzEgNTIgNDcgNjUgMzAgNGUgNTQgNGQgNmEgNDUgNzggNGQgMzEgNTIgMzk=` using our application! If you are new here, you can continue reading this guide to understand how to unravel this mysterious text! 6. For more information on Decodex's features, please refer to the Features Section. -## List of Available Modules +## List of Available [Modules](#moduleDefinition) Below, you can see the list of available modules and what they do. > :information_source: These are some of the more common encoding/decoding/cipher methods that can be found in CTF competitions. @@ -283,21 +286,35 @@ Example: ![recipeDelete.png](images/recipeDelete.png) -## Saving Recipe to File +## Storage of Recipe Files -Saves the recipe into a file. However, this is done automatically for you, so that it is less punishing for you (especially if you often forget to save your documents before exiting). +> ❗ :important: +> +> A recipe will correspond to a recipe file which is a plaintext file with the name `.txt` where `recipeName` is the name of the recipe. +> +> It is highly recommended that you only modify recipe files through the `recipe` commands in `Decodex` and not manually edit the recipe files. +> +> If you choose to manually edit the recipe files, they should not be edited while `Decodex` is running as `Decodex` could accidentally overwrite your changes. +> +> Take note that incorrectly edited files will be considered as invalid and will not be loaded on program start up. +> +> Additional note would be that the recipe files would not be loaded successfully if Decodex does not have access rights to the files, or it is not in the valid filetype. -How it works: +### Load Recipe Files on Startup -Every time you add a recipe into Decodex, it will save this new recipe into a text file, with the recipe name as the file name, in the `recipe/` folder. Furthermore, any adding or removing of modules from this recipe would also update the corresponding recipe save file. +Loads the recipes from the recipe files found in the `recipe/` directory. This is done automatically for you on startup so that you do not have to manually add multiple recipe files into Decodex each time. -Example: +### Save Recipe to File + +Saves the recipe to a recipe file in the `recipe/` directory. This is done automatically for you whenever a recipe is created or modified so that it is more convenient and less punishing for you (especially if you often forget to save your documents before exiting). + +### Delete Recipe File -If you create a new recipe called "onlyForMe" in Decodex, this will create "onlyForMe.txt" which you can find in the `recipe/` folder. And whenever you run the `recipe push` or `recipe pop` command, it will update "onlyForMe.txt" accordingly. +Deletes the recipe file in the `recipe/` directory. This is also done automatically for you (via the `recipe delete` command), so that you do not have to manually find the corresponding recipe file to delete it. -## Reading/writing data from/to file `[coming soon in the future]` +## Reading/Writing Data from/to File `[coming soon in the future]` -Coming soon to a cinema near you in the future*…* +Coming soon to a cinema near you in the future… ## Command Summary diff --git a/docs/images/quickStartPrompt.png b/docs/images/quickStartPrompt.png new file mode 100644 index 0000000000000000000000000000000000000000..cf921df8b7ba7cdf264c119a1b12c04bc9f2097b GIT binary patch literal 18496 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 z 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

BrfdFxYwGQ_%-y!h%<0TuFm^>A%fRVaX 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 Date: Wed, 27 Oct 2021 03:41:40 +0800 Subject: [PATCH 378/607] Add recipe saving to file --- src/main/java/decodex/Decodex.java | 10 ++- src/main/java/decodex/commands/Command.java | 3 +- .../commands/recipe/RecipeDeleteCommand.java | 3 +- .../commands/recipe/RecipeNewCommand.java | 3 +- .../commands/recipe/RecipePopCommand.java | 3 +- .../commands/recipe/RecipePushCommand.java | 3 +- .../commands/recipe/RecipeResetCommand.java | 3 +- .../java/decodex/recipes/RecipeManager.java | 25 +++++-- .../decodex/commands/HelpCommandTest.java | 4 +- .../decodex/commands/InputCommandTest.java | 7 +- .../decodex/commands/ListCommandTest.java | 4 +- .../commands/RecipeDeleteCommandTest.java | 12 ++-- .../commands/RecipeListCommandTest.java | 7 +- .../commands/RecipeNewCommandTest.java | 10 ++- .../commands/RecipePopCommandTest.java | 22 ++++--- .../commands/RecipePushCommandTest.java | 20 +++--- .../commands/RecipeResetCommandTest.java | 7 +- .../commands/RecipeSelectCommandTest.java | 13 ++-- .../decodex/commands/ResetCommandTest.java | 7 +- .../decodex/commands/SelectCommandTest.java | 33 ++++++---- .../decodex/recipes/RecipeManagerTest.java | 66 ++++++++++--------- 21 files changed, 164 insertions(+), 101 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 4fd35c8293..9447d6f3de 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -6,17 +6,15 @@ import decodex.data.exception.CommandException; import decodex.data.exception.DataManagerException; import decodex.data.exception.ModuleException; +import decodex.data.exception.ModuleManagerException; import decodex.data.exception.ParserException; import decodex.data.exception.RecipeException; import decodex.data.exception.RecipeManagerException; -import decodex.data.exception.ModuleManagerException; import decodex.modules.ModuleManager; import decodex.parser.Parser; -import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.storage.Storage; import decodex.ui.Ui; - import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -50,7 +48,7 @@ private void initDecodex() { parser = new Parser(); ui = new Ui(); storage = new Storage(); - recipeManager = new RecipeManager(); + recipeManager = new RecipeManager(storage); try { loadSavedRecipes(); } catch (IOException err) { @@ -78,8 +76,8 @@ public void run() { command = parser.parseCommand(userInput); assert command != null : "Command should not be null"; command.run(dataManager, moduleManager, ui, recipeManager); - } catch (ParserException | CommandException | ModuleManagerException - | DataManagerException | ModuleException | RecipeException | RecipeManagerException err) { + } catch (ParserException | CommandException | ModuleManagerException | DataManagerException | + ModuleException | RecipeException | RecipeManagerException | IOException err) { ui.printError(err); logger.fine(err.getMessage()); } diff --git a/src/main/java/decodex/commands/Command.java b/src/main/java/decodex/commands/Command.java index daf4777487..2a1634c809 100644 --- a/src/main/java/decodex/commands/Command.java +++ b/src/main/java/decodex/commands/Command.java @@ -10,6 +10,7 @@ import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; import decodex.ui.Ui; +import java.io.IOException; /** * Command serves as a foundation for other commands to be built upon. @@ -21,5 +22,5 @@ public Command() { public abstract void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) throws CommandException, ModuleManagerException, DataManagerException, ModuleException, RecipeException, - RecipeManagerException; + RecipeManagerException, IOException; } diff --git a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java index be939265c7..c25412167b 100644 --- a/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeDeleteCommand.java @@ -9,6 +9,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; +import java.io.IOException; public class RecipeDeleteCommand extends Command { @@ -25,7 +26,7 @@ public RecipeDeleteCommand(String recipeName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, ModuleException, RecipeManagerException { + throws CommandException, ModuleException, RecipeManagerException, IOException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } diff --git a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java index dd5aa1f4d8..a5ce1e2812 100644 --- a/src/main/java/decodex/commands/recipe/RecipeNewCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeNewCommand.java @@ -11,6 +11,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; +import java.io.IOException; public class RecipeNewCommand extends Command { @@ -27,7 +28,7 @@ public RecipeNewCommand(String recipeName) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, ModuleException, RecipeException, RecipeManagerException { + throws CommandException, ModuleException, RecipeException, RecipeManagerException, IOException { if (recipeName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_RECIPE_NAME); } diff --git a/src/main/java/decodex/commands/recipe/RecipePopCommand.java b/src/main/java/decodex/commands/recipe/RecipePopCommand.java index b843758e3d..68dce0e03f 100644 --- a/src/main/java/decodex/commands/recipe/RecipePopCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePopCommand.java @@ -9,6 +9,7 @@ import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; +import java.io.IOException; public class RecipePopCommand extends Command { @@ -22,7 +23,7 @@ public RecipePopCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws RecipeException, RecipeManagerException { + throws RecipeException, RecipeManagerException, IOException { Module module = recipeManager.popModuleFromEditedRecipe(); Recipe editingRecipe = recipeManager.getEditingRecipe(); ui.printModuleRemovedFromRecipe(module.getName(), editingRecipe.getName()); diff --git a/src/main/java/decodex/commands/recipe/RecipePushCommand.java b/src/main/java/decodex/commands/recipe/RecipePushCommand.java index 9c68498364..00c9356950 100644 --- a/src/main/java/decodex/commands/recipe/RecipePushCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipePushCommand.java @@ -12,6 +12,7 @@ import decodex.recipes.RecipeManager; import decodex.ui.Ui; import decodex.ui.messages.ErrorMessages; +import java.io.IOException; public class RecipePushCommand extends Command { @@ -30,7 +31,7 @@ public RecipePushCommand(String moduleName, String[] parameters) { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException { + throws CommandException, ModuleManagerException, ModuleException, RecipeManagerException, IOException { if (moduleName.isBlank()) { throw new CommandException(ErrorMessages.MISSING_MODULE_NAME); } diff --git a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java index 4c4ee0e639..6cf6d7bb8e 100644 --- a/src/main/java/decodex/commands/recipe/RecipeResetCommand.java +++ b/src/main/java/decodex/commands/recipe/RecipeResetCommand.java @@ -7,6 +7,7 @@ import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; import decodex.ui.Ui; +import java.io.IOException; public class RecipeResetCommand extends Command { @@ -20,7 +21,7 @@ public RecipeResetCommand() { @Override public void run(DataManager dataManager, ModuleManager moduleManager, Ui ui, RecipeManager recipeManager) - throws RecipeManagerException { + throws RecipeManagerException, IOException { Recipe editingRecipe = recipeManager.getEditingRecipe(); recipeManager.resetEditedRecipe(); ui.printRecipeReset(editingRecipe.getName()); diff --git a/src/main/java/decodex/recipes/RecipeManager.java b/src/main/java/decodex/recipes/RecipeManager.java index 1c03b2d5ec..a0a3e7eb5b 100644 --- a/src/main/java/decodex/recipes/RecipeManager.java +++ b/src/main/java/decodex/recipes/RecipeManager.java @@ -1,5 +1,7 @@ package decodex.recipes; +import decodex.storage.Storage; +import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Set; @@ -30,12 +32,15 @@ public class RecipeManager { */ private String editingRecipeName; + private Storage storage; + /** * Creates a new RecipeManager with no recipes. */ - public RecipeManager() { + public RecipeManager(Storage storage) { recipeList = new HashMap<>(); editingRecipeName = null; + this.storage = storage; } /** @@ -56,12 +61,13 @@ public String[] getRecipeNames() { * @param recipe The recipe to be added to the recipe manager. * @throws RecipeManagerException If the given recipe name already exists in recipe manager. */ - public void addRecipe(Recipe recipe) throws RecipeManagerException { + public void addRecipe(Recipe recipe) throws RecipeManagerException, IOException { if (recipeList.containsKey(recipe.getName())) { throw new RecipeManagerException(ErrorMessages.DUPLICATE_RECIPE_NAME_MESSAGE); } recipeList.put(recipe.getName(), recipe); logger.fine(String.format("[RecipeManager] Added recipe %s", recipe.getName())); + storage.saveRecipeToFile(recipe); } /** @@ -71,13 +77,14 @@ public void addRecipe(Recipe recipe) throws RecipeManagerException { * @return The recipe that was removed. * @throws RecipeManagerException If the recipe could not be found in the recipe manager. */ - public Recipe removeRecipe(String name) throws RecipeManagerException { + public Recipe removeRecipe(String name) throws RecipeManagerException, IOException { Recipe recipeToRemove = getRecipe(name); recipeList.remove(name); if (editingRecipeName != null && editingRecipeName.equals(name)) { editingRecipeName = null; } logger.fine(String.format("[RecipeManager] Removed recipe %s", recipeToRemove.getName())); + storage.deleteRecipeFile(name); return recipeToRemove; } @@ -128,9 +135,10 @@ public void selectRecipeForEditing(String name) throws RecipeManagerException { * @param module The module to be added to the recipe. * @throws RecipeManagerException If no recipe is selected as currently being edited. */ - public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerException { + public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerException, IOException { Recipe editingRecipe = getEditingRecipe(); editingRecipe.push(module); + storage.saveRecipeToFile(editingRecipe); } /** @@ -139,9 +147,11 @@ public void pushModuleIntoEditedRecipe(Module module) throws RecipeManagerExcept * @return The module that was popped from the recipe. * @throws RecipeManagerException If no recipe is selected as currently being edited. */ - public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeException { + public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeException, IOException { Recipe editingRecipe = getEditingRecipe(); - return editingRecipe.pop(); + Module module = editingRecipe.pop(); + storage.saveRecipeToFile(editingRecipe); + return module; } /** @@ -149,8 +159,9 @@ public Module popModuleFromEditedRecipe() throws RecipeManagerException, RecipeE * * @throws RecipeManagerException If no recipe is selected as currently being edited. */ - public void resetEditedRecipe() throws RecipeManagerException { + public void resetEditedRecipe() throws RecipeManagerException, IOException { Recipe editingRecipe = getEditingRecipe(); editingRecipe.reset(); + storage.saveRecipeToFile(editingRecipe); } } diff --git a/src/test/java/decodex/commands/HelpCommandTest.java b/src/test/java/decodex/commands/HelpCommandTest.java index 3c1602e97d..b58f2dba75 100644 --- a/src/test/java/decodex/commands/HelpCommandTest.java +++ b/src/test/java/decodex/commands/HelpCommandTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; +import decodex.storage.Storage; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -21,7 +22,8 @@ class HelpCommandTest { private final DataManager dataManager = new DataManager(); private final ModuleManager moduleManager = new ModuleManager(); - private final RecipeManager recipeManager = new RecipeManager(); + private final Storage storage = new Storage(); + private final RecipeManager recipeManager = new RecipeManager(storage); private final Ui ui = new Ui(); @BeforeEach diff --git a/src/test/java/decodex/commands/InputCommandTest.java b/src/test/java/decodex/commands/InputCommandTest.java index bc7747d237..5fe32b3256 100644 --- a/src/test/java/decodex/commands/InputCommandTest.java +++ b/src/test/java/decodex/commands/InputCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.storage.Storage; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -21,7 +22,8 @@ public class InputCommandTest { public void run_missingArguments_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String dataString = ""; InputCommand testCommand = new InputCommand(dataString); @@ -33,7 +35,8 @@ public void run_insertData_insertedData() throws CommandException, DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String dataString = "something"; InputCommand testCommand = new InputCommand(dataString); diff --git a/src/test/java/decodex/commands/ListCommandTest.java b/src/test/java/decodex/commands/ListCommandTest.java index b7f87572e2..9681991c3f 100644 --- a/src/test/java/decodex/commands/ListCommandTest.java +++ b/src/test/java/decodex/commands/ListCommandTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import decodex.storage.Storage; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -23,7 +24,8 @@ public class ListCommandTest { private final DataManager dataManager = new DataManager(); private final ModuleManager moduleManager = new ModuleManager(); - private final RecipeManager recipeManager = new RecipeManager(); + private final Storage storage = new Storage(); + private final RecipeManager recipeManager = new RecipeManager(storage); private final Ui ui = new Ui(); @BeforeEach diff --git a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java index 701849073c..4a0ede3ef9 100644 --- a/src/test/java/decodex/commands/RecipeDeleteCommandTest.java +++ b/src/test/java/decodex/commands/RecipeDeleteCommandTest.java @@ -13,20 +13,23 @@ import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import java.io.IOException; import org.junit.jupiter.api.Test; class RecipeDeleteCommandTest { @Test void run_deleteValidRecipe_recipeDeleted() throws CommandException, RecipeException, ModuleException, - RecipeManagerException, DataManagerException, ModuleManagerException { + RecipeManagerException, DataManagerException, ModuleManagerException, IOException { String testRecipeName = "BaconPancakes"; DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); recipeManager.addRecipe(new Recipe(testRecipeName)); @@ -37,11 +40,12 @@ void run_deleteValidRecipe_recipeDeleted() throws CommandException, RecipeExcept } @Test - void run_deleteInvalidRecipe_expectException() throws RecipeException, RecipeManagerException { + void run_deleteInvalidRecipe_expectException() throws RecipeException, RecipeManagerException, IOException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); recipeManager.addRecipe(new Recipe("BaconPancakes")); diff --git a/src/test/java/decodex/commands/RecipeListCommandTest.java b/src/test/java/decodex/commands/RecipeListCommandTest.java index 7509107f47..212739aa2c 100644 --- a/src/test/java/decodex/commands/RecipeListCommandTest.java +++ b/src/test/java/decodex/commands/RecipeListCommandTest.java @@ -16,8 +16,10 @@ import decodex.modules.rot.RotEncoder; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.PrintStream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,11 +34,12 @@ class RecipeListCommandTest { private final DataManager dataManager = new DataManager(); private final ModuleManager moduleManager = new ModuleManager(); - private final RecipeManager recipeManager = new RecipeManager(); + private final Storage storage = new Storage(); + private final RecipeManager recipeManager = new RecipeManager(storage); private final Ui ui = new Ui(); @BeforeEach - public void createTestRecipe() throws RecipeManagerException, RecipeException { + public void createTestRecipe() throws RecipeManagerException, RecipeException, IOException { Recipe recipe = new Recipe(TEST_RECIPE_NAME); recipe.push(new HexEncoder()); diff --git a/src/test/java/decodex/commands/RecipeNewCommandTest.java b/src/test/java/decodex/commands/RecipeNewCommandTest.java index b0954fe38f..ade121b14e 100644 --- a/src/test/java/decodex/commands/RecipeNewCommandTest.java +++ b/src/test/java/decodex/commands/RecipeNewCommandTest.java @@ -13,21 +13,24 @@ import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import java.io.IOException; import org.junit.jupiter.api.Test; class RecipeNewCommandTest { @Test void run_createNewRecipe_newRecipeCreated() throws CommandException, RecipeException, ModuleException, - RecipeManagerException, DataManagerException, ModuleManagerException { + RecipeManagerException, DataManagerException, ModuleManagerException, IOException { String testRecipeName = "BaconPancakes"; Command newRecipeCommand = new RecipeNewCommand(testRecipeName); DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager); @@ -42,7 +45,8 @@ void run_createNewRecipeWithNoName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); assertThrows(CommandException.class, () -> newRecipeCommand.run(dataManager, moduleManager, ui, recipeManager)); diff --git a/src/test/java/decodex/commands/RecipePopCommandTest.java b/src/test/java/decodex/commands/RecipePopCommandTest.java index a9f3e9921c..9339f83a34 100644 --- a/src/test/java/decodex/commands/RecipePopCommandTest.java +++ b/src/test/java/decodex/commands/RecipePopCommandTest.java @@ -4,26 +4,27 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import decodex.commands.recipe.RecipePopCommand; -import decodex.data.exception.RecipeException; -import org.junit.jupiter.api.Test; - import decodex.data.DataManager; import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeManagerException; import decodex.data.exception.ModuleManagerException; -import decodex.modules.ModuleManager; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; import decodex.modules.Module; +import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import java.io.IOException; +import org.junit.jupiter.api.Test; class RecipePopCommandTest { @Test public void run_oneModuleToEditingRecipe_recipeSizeIsZero() - throws RecipeManagerException, ModuleManagerException, ModuleException, RecipeException { - - RecipeManager recipeManager = new RecipeManager(); + throws RecipeManagerException, ModuleManagerException, ModuleException, RecipeException, IOException { + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); String testRecipeName = "test"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -44,10 +45,11 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsZero() } @Test - public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException { + public void run_emptyEditingRecipe_expectException() throws RecipeException, RecipeManagerException, IOException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String testRecipeName = "test"; diff --git a/src/test/java/decodex/commands/RecipePushCommandTest.java b/src/test/java/decodex/commands/RecipePushCommandTest.java index 4da4477566..1383ad47a9 100644 --- a/src/test/java/decodex/commands/RecipePushCommandTest.java +++ b/src/test/java/decodex/commands/RecipePushCommandTest.java @@ -3,28 +3,31 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import decodex.data.exception.RecipeException; -import org.junit.jupiter.api.Test; - import decodex.commands.recipe.RecipePushCommand; import decodex.data.DataManager; import decodex.data.exception.CommandException; import decodex.data.exception.ModuleException; -import decodex.data.exception.RecipeManagerException; import decodex.data.exception.ModuleManagerException; +import decodex.data.exception.RecipeException; +import decodex.data.exception.RecipeManagerException; import decodex.modules.ModuleManager; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import java.io.IOException; +import org.junit.jupiter.api.Test; class RecipePushCommandTest { @Test public void run_oneModuleToEditingRecipe_recipeSizeIsOne() - throws RecipeException, RecipeManagerException, CommandException, ModuleException, ModuleManagerException { + throws RecipeException, RecipeManagerException, CommandException, + ModuleException, ModuleManagerException, IOException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String testRecipeName = "test"; @@ -42,10 +45,11 @@ public void run_oneModuleToEditingRecipe_recipeSizeIsOne() } @Test - public void run_emptyModuleName_expectException() throws RecipeException, RecipeManagerException { + public void run_emptyModuleName_expectException() throws RecipeException, RecipeManagerException, IOException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String testRecipeName = "test"; diff --git a/src/test/java/decodex/commands/RecipeResetCommandTest.java b/src/test/java/decodex/commands/RecipeResetCommandTest.java index 23be87dce8..239833f2bf 100644 --- a/src/test/java/decodex/commands/RecipeResetCommandTest.java +++ b/src/test/java/decodex/commands/RecipeResetCommandTest.java @@ -3,6 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import decodex.data.exception.RecipeException; +import decodex.storage.Storage; +import java.io.IOException; import org.junit.jupiter.api.Test; import decodex.commands.recipe.RecipeResetCommand; @@ -20,8 +22,9 @@ class RecipeResetCommandTest { @Test public void run_oneModuleInEditingRecipe_recipeSizeIsZero() throws RecipeException, RecipeManagerException, - ModuleManagerException, ModuleException { - RecipeManager recipeManager = new RecipeManager(); + ModuleManagerException, ModuleException, IOException { + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); String testRecipeName = "test"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); diff --git a/src/test/java/decodex/commands/RecipeSelectCommandTest.java b/src/test/java/decodex/commands/RecipeSelectCommandTest.java index 3449e3fd2e..9a03c5268b 100644 --- a/src/test/java/decodex/commands/RecipeSelectCommandTest.java +++ b/src/test/java/decodex/commands/RecipeSelectCommandTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import decodex.data.exception.RecipeException; +import decodex.storage.Storage; +import java.io.IOException; import org.junit.jupiter.api.Test; import decodex.commands.recipe.RecipeSelectCommand; @@ -19,10 +21,11 @@ class RecipeSelectCommandTest { @Test public void run_selectExistingRecipe_existingRecipe() throws RecipeException, RecipeManagerException, - CommandException { + CommandException, IOException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String testRecipeName = "test"; @@ -38,7 +41,8 @@ public void run_selectExistingRecipe_existingRecipe() throws RecipeException, Re public void run_selectNonExistentRecipe_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String testRecipeName = "test"; @@ -52,7 +56,8 @@ public void run_selectNonExistentRecipe_expectException() { public void run_nullRecipe_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String testRecipeName = ""; diff --git a/src/test/java/decodex/commands/ResetCommandTest.java b/src/test/java/decodex/commands/ResetCommandTest.java index 2c1f7751e6..7213a66025 100644 --- a/src/test/java/decodex/commands/ResetCommandTest.java +++ b/src/test/java/decodex/commands/ResetCommandTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; +import decodex.storage.Storage; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -20,7 +21,8 @@ class ResetCommandTest { public void run_noChanges_sameOriginalData() throws DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); Data originalData = new Data("hi"); @@ -35,7 +37,8 @@ public void run_noChanges_sameOriginalData() throws DataManagerException { public void run_newData_sameOriginalData() throws DataManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); Data originalData = new Data("hi"); diff --git a/src/test/java/decodex/commands/SelectCommandTest.java b/src/test/java/decodex/commands/SelectCommandTest.java index eec05bb6b2..251f709eed 100644 --- a/src/test/java/decodex/commands/SelectCommandTest.java +++ b/src/test/java/decodex/commands/SelectCommandTest.java @@ -13,8 +13,10 @@ import decodex.modules.hex.HexEncoder; import decodex.recipes.Recipe; import decodex.recipes.RecipeManager; +import decodex.storage.Storage; import decodex.ui.Ui; +import java.io.IOException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -26,7 +28,8 @@ public void run_validModuleName_success() throws ModuleManagerException, Command ModuleException, RecipeException, RecipeManagerException { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -47,7 +50,8 @@ public void run_moduleWithParameters_success() throws ModuleManagerException, Co DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); Ui ui = new Ui(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Data data = new Data("hello world"); dataManager.setOriginalData(data); @@ -65,7 +69,8 @@ public void run_moduleWithParameters_success() throws ModuleManagerException, Co public void run_blankModuleName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -83,7 +88,8 @@ public void run_blankModuleName_expectException() { public void run_unknownModuleName_expectException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); Data data = new Data("hello world"); @@ -100,12 +106,13 @@ public void run_unknownModuleName_expectException() { @Test public void run_validRecipeName_success() throws ModuleManagerException, CommandException, DataManagerException, - ModuleException, RecipeException, RecipeManagerException { + ModuleException, RecipeException, RecipeManagerException, IOException { DataManager dataManager = new DataManager(); Data data = new Data("hello world"); dataManager.setOriginalData(data); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); String recipeName = "testRecipe"; Recipe recipe = new Recipe(recipeName); recipe.push(new HexEncoder()); @@ -125,13 +132,14 @@ public void run_validRecipeName_success() throws ModuleManagerException, Command @Test public void run_emptyRecipe_success() throws ModuleManagerException, CommandException, DataManagerException, - ModuleException, RecipeException, RecipeManagerException { + ModuleException, RecipeException, RecipeManagerException, IOException { DataManager dataManager = new DataManager(); String dataString = "hello world"; Data data = new Data(dataString); dataManager.setOriginalData(data); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); String recipeName = "testRecipe"; Recipe recipe = new Recipe(recipeName); recipeManager.addRecipe(recipe); @@ -151,7 +159,8 @@ public void run_emptyRecipe_success() throws ModuleManagerException, CommandExce public void run_blankRecipeName_expectRecipeManagerException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String selectCategory = "recipe"; @@ -167,7 +176,8 @@ public void run_blankRecipeName_expectRecipeManagerException() { public void run_unknownRecipe_expectRecipeManagerException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String selectCategory = "recipe"; @@ -183,7 +193,8 @@ public void run_unknownRecipe_expectRecipeManagerException() { public void run_invalidSelectCategory_expectCommandException() { DataManager dataManager = new DataManager(); ModuleManager moduleManager = new ModuleManager(); - RecipeManager recipeManager = new RecipeManager(); + Storage storage = new Storage(); + RecipeManager recipeManager = new RecipeManager(storage); Ui ui = new Ui(); String selectCategory = "unknownCategory"; diff --git a/src/test/java/decodex/recipes/RecipeManagerTest.java b/src/test/java/decodex/recipes/RecipeManagerTest.java index 4fcf7d6e0f..13b1569e51 100644 --- a/src/test/java/decodex/recipes/RecipeManagerTest.java +++ b/src/test/java/decodex/recipes/RecipeManagerTest.java @@ -3,18 +3,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import org.junit.jupiter.api.Test; - -import decodex.data.exception.RecipeManagerException; import decodex.data.exception.RecipeException; -import decodex.modules.hex.HexEncoder; +import decodex.data.exception.RecipeManagerException; import decodex.modules.Module; +import decodex.modules.hex.HexEncoder; +import decodex.storage.Storage; +import decodex.ui.Ui; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; class RecipeManagerTest { + private final Storage storage = new Storage(); + private RecipeManager recipeManager; + + @BeforeEach + public void createNewRecipeManager() { + recipeManager = new RecipeManager(storage); + } + @Test - void addRecipe_repeatedRecipeName_expectException() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void addRecipe_repeatedRecipeName_expectException() throws RecipeException, RecipeManagerException, IOException { Recipe newRecipe = new Recipe("sameRecipeName"); recipeManager.addRecipe(newRecipe); @@ -23,8 +33,8 @@ void addRecipe_repeatedRecipeName_expectException() throws RecipeException, Reci } @Test - void removeRecipe_oneRecipe_removedRecipeIsTheOneRecipe() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void removeRecipe_oneRecipe_removedRecipeIsTheOneRecipe() + throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -34,14 +44,13 @@ void removeRecipe_oneRecipe_removedRecipeIsTheOneRecipe() throws RecipeException @Test void removeRecipe_noRecipe_expectException() { - RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; assertThrows(RecipeManagerException.class, () -> recipeManager.removeRecipe(testRecipeName)); } @Test - void removeRecipe_recipeIsEditedRecipe_expectException() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void removeRecipe_recipeIsEditedRecipe_expectException() + throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -52,8 +61,7 @@ void removeRecipe_recipeIsEditedRecipe_expectException() throws RecipeException, } @Test - void getRecipe_oneRecipe_managerContainOneRecipe() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void getRecipe_oneRecipe_managerContainOneRecipe() throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -64,14 +72,12 @@ void getRecipe_oneRecipe_managerContainOneRecipe() throws RecipeException, Recip @Test void getRecipe_noRecipe_expectException() { - RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; assertThrows(RecipeManagerException.class, () -> recipeManager.getRecipe(testRecipeName)); } @Test - void getEditedRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void getEditedRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -82,15 +88,13 @@ void getEditedRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, Reci @Test void getEditedRecipe_noEditedRecipe_expectException() { - RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipeForEditing(testRecipeName)); } @Test - void selectRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void selectRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -101,15 +105,14 @@ void selectRecipe_oneEditedRecipe_editedRecipe() throws RecipeException, RecipeM @Test void selectRecipe_nonExistentRecipeName_expectException() { - RecipeManager recipeManager = new RecipeManager(); String testRecipeName = "testRecipeName"; assertThrows(RecipeManagerException.class, () -> recipeManager.selectRecipeForEditing(testRecipeName)); } @Test - void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() + throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -123,8 +126,8 @@ void pushModuleIntoEditedRecipe_oneEditedRecipe_oneModuele() throws RecipeExcept } @Test - void pushModuleIntoEditedRecipe_noEditedRecipe_expectException() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void pushModuleIntoEditedRecipe_noEditedRecipe_expectException() + throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -135,8 +138,8 @@ void pushModuleIntoEditedRecipe_noEditedRecipe_expectException() throws RecipeEx } @Test - void popModuleFromEditedRecipe_oneEditedRecipe_poppedModule() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void popModuleFromEditedRecipe_oneEditedRecipe_poppedModule() + throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); Module insertedModule = new HexEncoder(); @@ -149,8 +152,8 @@ void popModuleFromEditedRecipe_oneEditedRecipe_poppedModule() throws RecipeExcep } @Test - void popModuleFromEditedRecipe_noEditedRecipe_expectException() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void popModuleFromEditedRecipe_noEditedRecipe_expectException() + throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); Module insertedModule = new HexEncoder(); @@ -161,8 +164,7 @@ void popModuleFromEditedRecipe_noEditedRecipe_expectException() throws RecipeExc } @Test - void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); @@ -174,8 +176,8 @@ void resetEditedRecipe_oneEditedRecipe_noError() throws RecipeException, RecipeM } @Test - void resetEditedRecipe_noEditedRecipe_expectException() throws RecipeException, RecipeManagerException { - RecipeManager recipeManager = new RecipeManager(); + void resetEditedRecipe_noEditedRecipe_expectException() + throws RecipeException, RecipeManagerException, IOException { String testRecipeName = "testRecipeName"; Recipe testRecipe = new Recipe(testRecipeName); recipeManager.addRecipe(testRecipe); From 8b8576e83790a9560b399058ba4bf218f421d52c Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 03:48:57 +0800 Subject: [PATCH 379/607] Update prompt with editing recipe --- src/main/java/decodex/Decodex.java | 7 +++++++ src/main/java/decodex/ui/Ui.java | 20 ++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 4fd35c8293..4b1bc0f001 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -72,6 +72,13 @@ public void run() { Command command = null; do { + String editingRecipeName; + try { + editingRecipeName = recipeManager.getEditingRecipe().getName(); + } catch (RecipeManagerException e) { + editingRecipeName = null; + } + ui.printPromptHeader(editingRecipeName); String userInput = ui.readInput(); logger.fine("User input: " + userInput); try { diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index a64a941cd5..9274414cd7 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -12,7 +12,9 @@ */ public class Ui { - private static final String PROMPT_HEADER = "Decodex > "; + private static final String PROMPT_HEADER = "Decodex [%s] > "; + private static final String PROMPT_EDITING = "Editing: "; + private static final String BLANK_STRING = ""; private static final String SUCCESS_ICON = "[+]"; private static final String ERROR_ICON = "[x]"; private static final String INPUT_PREFIX = "Input:"; @@ -44,7 +46,6 @@ public void printGoodbye() { * @return String of the user input. */ public String readInput() { - printPromptHeader(); return in.nextLine(); } @@ -220,6 +221,11 @@ public void printNewRecipeCreated(String recipeName) { printSuccess(String.format(RegularMessages.NEW_RECIPE_CREATED, recipeName)); } + /** + * Prints the list of messages of recipe files that failed to load. + * + * @param failedFilesMessageList The list of messages. + */ public void printFailedToLoadFromStorageMessage(ArrayList failedFilesMessageList) { for (String failedFileMessage : failedFilesMessageList) { System.out.println(failedFileMessage); @@ -228,9 +234,15 @@ public void printFailedToLoadFromStorageMessage(ArrayList failedFilesMes /** * Prints the prompt header. + * + * @param editingRecipeName Name of the recipe in editing. */ - private void printPromptHeader() { - System.out.print(PROMPT_HEADER); + public void printPromptHeader(String editingRecipeName) { + if (editingRecipeName == null) { + System.out.printf(PROMPT_HEADER, BLANK_STRING); + } else { + System.out.printf(PROMPT_HEADER, PROMPT_EDITING + editingRecipeName); + } } /** From ec387e204e00521ac56214321e5f730eb7ce3288 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 12:59:31 +0800 Subject: [PATCH 380/607] Update text-ui-test --- text-ui-test/EXPECTED.TXT | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index b726f771c3..48c3297f51 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,18 +1,18 @@ ======================== Decodex =========================== Welcome to Decodex, transforming data with ease ============================================================ -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Unknown command, please enter a valid command -Decodex > [x] Your command cannot be blank -Decodex > [+] Input: "I am a lab rat" -Decodex > [+] Output: "SSBhbSBhIGxhYiByYXQ=" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "4920616d2061206c616220726174" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Output: "L dp d ode udw" -Decodex > [+] Output: "I am a lab rat" -Decodex > [+] Reverted all changes -Decodex > Here is the list of supported modules: +Decodex [] > [x] Unknown command, please enter a valid command +Decodex [] > [x] Unknown command, please enter a valid command +Decodex [] > [x] Your command cannot be blank +Decodex [] > [+] Input: "I am a lab rat" +Decodex [] > [+] Output: "SSBhbSBhIGxhYiByYXQ=" +Decodex [] > [+] Output: "I am a lab rat" +Decodex [] > [+] Output: "4920616d2061206c616220726174" +Decodex [] > [+] Output: "I am a lab rat" +Decodex [] > [+] Output: "L dp d ode udw" +Decodex [] > [+] Output: "I am a lab rat" +Decodex [] > [+] Reverted all changes +Decodex [] > Here is the list of supported modules: base64encode - Encodes the data using Base64 format. base64decode - Decodes the data using Base64 format. hexencode - Converts the input string to hexadecimal bytes @@ -23,17 +23,17 @@ Decodex > Here is the list of supported modules: You do not have any recipes -Decodex > [x] The given recipe could not be found. -Decodex > [+] Recipe testRecipe created successfully -Decodex > Here is the list of available recipes: +Decodex [] > [x] The given recipe could not be found. +Decodex [] > [+] Recipe testRecipe created successfully +Decodex [Editing: testRecipe] > Here is the list of available recipes: testRecipe -Decodex > [+] There are no modules in the recipe +Decodex [Editing: testRecipe] > [+] There are no modules in the recipe [+] Output: "I am a lab rat" -Decodex > [+] Added hexencode module to recipe testRecipe -Decodex > [+] Added base64encode module to recipe testRecipe -Decodex > [+] Added rotencode module to recipe testRecipe -Decodex > [+] Modules executed: hexencode, base64encode, rotencode +Decodex [Editing: testRecipe] > [+] Added hexencode module to recipe testRecipe +Decodex [Editing: testRecipe] > [+] Added base64encode module to recipe testRecipe +Decodex [Editing: testRecipe] > [+] Added rotencode module to recipe testRecipe +Decodex [Editing: testRecipe] > [+] Modules executed: hexencode, base64encode, rotencode [+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" -Decodex > [x] Unknown command, please enter a valid command -Decodex > Goodbye! +Decodex [Editing: testRecipe] > [x] Unknown command, please enter a valid command +Decodex [Editing: testRecipe] > Goodbye! From c8dda71ec8bfe97ca25c88943e0c1224948eb85b Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 13:04:10 +0800 Subject: [PATCH 381/607] Update JavaDocs --- src/main/java/decodex/ui/Ui.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/ui/Ui.java b/src/main/java/decodex/ui/Ui.java index 9274414cd7..9516829831 100644 --- a/src/main/java/decodex/ui/Ui.java +++ b/src/main/java/decodex/ui/Ui.java @@ -222,9 +222,9 @@ public void printNewRecipeCreated(String recipeName) { } /** - * Prints the list of messages of recipe files that failed to load. + * Prints the list of message for the recipe files that failed to load. * - * @param failedFilesMessageList The list of messages. + * @param failedFilesMessageList The list of messages for the failed recipe files. */ public void printFailedToLoadFromStorageMessage(ArrayList failedFilesMessageList) { for (String failedFileMessage : failedFilesMessageList) { From 36287ceb2bd1e6fe4f93c64148e2f648a1795c67 Mon Sep 17 00:00:00 2001 From: Sean Phang Date: Wed, 27 Oct 2021 13:14:52 +0800 Subject: [PATCH 382/607] Fix checkstyle --- src/main/java/decodex/Decodex.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/decodex/Decodex.java b/src/main/java/decodex/Decodex.java index 9447d6f3de..ee6c457d6f 100644 --- a/src/main/java/decodex/Decodex.java +++ b/src/main/java/decodex/Decodex.java @@ -76,8 +76,8 @@ public void run() { command = parser.parseCommand(userInput); assert command != null : "Command should not be null"; command.run(dataManager, moduleManager, ui, recipeManager); - } catch (ParserException | CommandException | ModuleManagerException | DataManagerException | - ModuleException | RecipeException | RecipeManagerException | IOException err) { + } catch (ParserException | CommandException | ModuleManagerException | DataManagerException + | ModuleException | RecipeException | RecipeManagerException | IOException err) { ui.printError(err); logger.fine(err.getMessage()); } From 85f1ea2ad7f3f3bf714187754e48ea941fc776e7 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 13:15:40 +0800 Subject: [PATCH 383/607] Update text-ui-test --- text-ui-test/EXPECTED.TXT | 9 ++++++++- text-ui-test/input.txt | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 48c3297f51..e950698c43 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -28,12 +28,19 @@ Decodex [] > [+] Recipe testRecipe created successfully Decodex [Editing: testRecipe] > Here is the list of available recipes: testRecipe +Decodex [Editing: testRecipe] > Here is the list of modules in recipe testRecipe: +There are no modules in the recipe Decodex [Editing: testRecipe] > [+] There are no modules in the recipe [+] Output: "I am a lab rat" Decodex [Editing: testRecipe] > [+] Added hexencode module to recipe testRecipe Decodex [Editing: testRecipe] > [+] Added base64encode module to recipe testRecipe Decodex [Editing: testRecipe] > [+] Added rotencode module to recipe testRecipe -Decodex [Editing: testRecipe] > [+] Modules executed: hexencode, base64encode, rotencode +Decodex [Editing: testRecipe] > Here is the list of modules in recipe testRecipe: + 1. hexencode + 2. base64encode + 3. rotencode 3 + +Decodex [Editing: testRecipe] > [+] Modules executed: hexencode, base64encode, rotencode 3 [+] Output: "QGnbPGBaQpTbPGBaPmD2BcBaQmLbPGfbQmH3QD==" Decodex [Editing: testRecipe] > [x] Unknown command, please enter a valid command Decodex [Editing: testRecipe] > Goodbye! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 554f26e80d..b88b402e08 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -14,11 +14,11 @@ select recipe testRecipe recipe new testRecipe list recipes recipe list -recipe list testRecipe select recipe testRecipe recipe push hexencode recipe push base64encode recipe push rotencode 3 +recipe list testRecipe select recipe testRecipe bye exit From d40c6e3920714b1ef7b9addea72add28c7ecb328 Mon Sep 17 00:00:00 2001 From: arraysius <16810847+arraysius@users.noreply.github.com> Date: Wed, 27 Oct 2021 13:28:44 +0800 Subject: [PATCH 384/607] Fix typo in comment --- docs/images/dg/architecture.png | Bin 88592 -> 88567 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/dg/architecture.png b/docs/images/dg/architecture.png index ba4f1d812a65f8423a81fb45933f212bb90a8a9f..4945dcc4124212b5b56bf4384073f9d71e478713 100644 GIT binary patch literal 88567 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 E0BV(y?f?J) literal 88592 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