Skip to content

Commit

Permalink
Add 2 new commands, rebind and unbind
Browse files Browse the repository at this point in the history
Added the ability to rebind commands to a custom string (must not contain whitespaces), and use them as shorthand. The binds can also be removed.
  • Loading branch information
ruiyangzh committed Sep 14, 2023
1 parent 0d1f348 commit 16efbff
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 8 deletions.
55 changes: 55 additions & 0 deletions src/main/java/command/BindingExecutable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package command;

import duke.TaskList;
import duke.UserInterface;

/**
* Represents an executable that either binds or unbinds a
*/
public class BindingExecutable implements Executable {
private final boolean successState;
private final boolean boundState;
private final String sourceBinding;
private final String customBinding;

/**
* Creates a new binding executable instance.
* @param wasSuccessful whether the binding was successful.
* @param wasBound whether an unbinding or rebinding was called.
* @param sourceBinding The original binding command string.
* @param customBinding THe custom binding command string.
*/
public BindingExecutable(boolean wasSuccessful, boolean wasBound, String sourceBinding, String customBinding) {
this.successState = wasSuccessful;
this.boundState = wasBound;
this.sourceBinding = sourceBinding;
this.customBinding = customBinding;
}

/**
* Executes and prints out to the ui the result of the bind command.
* @param list the list associated with the bot (unnecessary.)
* @param ui the ui to output to.
* @return false, since the bot does not shut down.
*/
@Override
public boolean execute(TaskList list, UserInterface ui) {
StringBuilder outputBuilder = new StringBuilder();
if (boundState) {
outputBuilder.append("Adding of custom binding ");
} else {
outputBuilder.append("Removal of custom binding ");
}
outputBuilder.append("\"").append(customBinding).append("\"");
outputBuilder.append(" to the source binding ");
outputBuilder.append("\"").append(sourceBinding).append("\" was ");
if (successState) {
outputBuilder.append("successful.");
} else {
outputBuilder.append("unsuccessful.");
}
ui.output(outputBuilder.toString());
return false;
}

}
6 changes: 4 additions & 2 deletions src/main/java/command/HelpExecutable.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ public boolean execute(TaskList list, UserInterface ui) {
+ "event: event (String name) /from (Date start) /to (Date end); creates event\n"
+ "todo: todo (String name); creates todo\n"
+ "deadline: deadline (String name) /by (Date deadline); creates deadline\n"
+ "delete: delete(int x); deletes the indicated task\n"
+ "find: find(String keyword); finds any tasks that contain the keyword\n"
+ "delete: delete (int x); deletes the indicated task\n"
+ "find: find (String keyword); finds any tasks that contain the keyword\n"
+ "rebind: rebind (String sourceBinding) /to (String customBinding); adds an alias for a command\n"
+ "unbind: unbind (String customBinding) /from (String sourceBinding); removes the alias\n"
);
return false;
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/duke/Duke.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ public Duke() {
}


/**
* Starts the application.
* @param stage the primary stage for this application, onto which
* the application scene can be set.
* Applications may create other stages, if needed, but they will not be
* primary stages.
*/
@Override
public void start(Stage stage) {
stage.setScene(ui.makeScene());
Expand Down
68 changes: 62 additions & 6 deletions src/main/java/duke/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.regex.Pattern;

import command.AddTaskExecutable;
import command.BindingExecutable;
import command.ClearExecutable;
import command.DeleteExecutable;
import command.Executable;
Expand All @@ -28,13 +29,14 @@
*/

public class Parser {
private final HashMap<String, ParserFunction> stringToCommand;
private static final HashMap<String, ParserFunction> stringToCommand = new HashMap<>();
private final HashMap<String, ParserFunction> customCommand;

/**
* Initializes the parser.
*/
public Parser() {
stringToCommand = new HashMap<>();
this.customCommand = new HashMap<>();
init();
}

Expand All @@ -53,6 +55,8 @@ private void init() {
stringToCommand.put("mark", Parser::parseMarkParams);
stringToCommand.put("unmark", Parser::parseUnmarkParams);
stringToCommand.put("find", Parser::parseFindParams);
stringToCommand.put("rebind", this::parseRebindParams);
stringToCommand.put("unbind", this::parseUnbindParams);
//Remember to add an entry into the hashmap whenever a command is added.
}

Expand All @@ -69,10 +73,33 @@ public Executable parse(String input) throws InvalidCommandException, InvalidVar
String commandIdentifier = matcher.group(1);
String paramString = matcher.group(2);
ParserFunction parsable = stringToCommand.get(commandIdentifier);
checkIfInvalid(parsable);
if (isInvalid(parsable)) {
parsable = parseCustom(commandIdentifier);
}
return parsable.apply(paramString);
}

/**
* Checks the custom command list for the command identifier.
* @param commandIdentifier the string to be searched for.
* @return the function mapped to the string if it exists.
* @throws InvalidCommandException if there is no matching function.
*/
private ParserFunction parseCustom(String commandIdentifier) throws InvalidCommandException {
ParserFunction func = this.customCommand.get(commandIdentifier);
if (isInvalid(func)) {
throw new InvalidCommandException("Unrecognized command!");
}
return func;
}

/**
* Produces a matcher that is guaranteed successful.
* @param input the string to match to a pattern.
* @param regex the pattern that the input should follow.
* @return A successful Matcher of the input to the regex.
* @throws InvalidVarException if the matching was unsuccessful.
*/
private static Matcher matchString(String input, String regex) throws InvalidVarException {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
Expand All @@ -82,6 +109,12 @@ private static Matcher matchString(String input, String regex) throws InvalidVar
return matcher;
}

/**
* Parses a string as a boolean if valid.
* @param boolString the string to be parsed.
* @return either true or false, depending on the string.
* @throws InvalidVarException if the string does not match the format.
*/
private static boolean parseBoolString(String boolString) throws InvalidVarException {
if (boolString.equals("TRUE")) {
return true;
Expand Down Expand Up @@ -175,10 +208,33 @@ private static Executable parseFindParams(String paramString) throws InvalidVarE
checkNonEmpty(paramString);
return new FindExecutable(paramString);
}
private static void checkIfInvalid(ParserFunction func) throws InvalidCommandException {
if (func == null) {
throw new InvalidCommandException("No such command found!");
private Executable parseRebindParams(String paramString) throws InvalidVarException {
String rebindRegex = "(\\S*)\\s/to\\s(\\S*)";
Matcher matcher = matchString(paramString, rebindRegex);
String sourceBinding = matcher.group(1);
String customBinding = matcher.group(2);
ParserFunction parserFunc = stringToCommand.get(sourceBinding);
boolean isValidCommand = (stringToCommand.get(sourceBinding) != null);
if (isValidCommand) {
this.customCommand.put(customBinding, parserFunc);
}
return new BindingExecutable(isValidCommand, true, sourceBinding, customBinding);
}
private Executable parseUnbindParams(String paramString) throws InvalidVarException {
String rebindRegex = "(\\S*)\\s/from\\s(\\S*)";
Matcher matcher = matchString(paramString, rebindRegex);
String sourceBinding = matcher.group(2);
String customBinding = matcher.group(1);
boolean isCurrentlyBound = (this.customCommand.get(customBinding) != null);
boolean isCorrectSource = (stringToCommand.get(sourceBinding) == this.customCommand.get(customBinding));
boolean successState = isCurrentlyBound && isCorrectSource;
if (successState) {
this.customCommand.remove(customBinding);
}
return new BindingExecutable(successState, false, sourceBinding, customBinding);
}
private static boolean isInvalid(ParserFunction func) {
return func == null;
}
private static ToDo todoFromString(String string) throws InvalidVarException {
String todoRegex = "(.*)" + Task.DIVIDER + "(.*)";
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/duke/UserInterface.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public void output(String output) {
dialogContainer.getChildren().add(dukeDialog);
}

/**
* Creates the ui elements and saves them to the instance's variables.
*/
private void spawnUiElements() {
scrollPane = new ScrollPane();
dialogContainer = new VBox();
Expand All @@ -90,6 +93,9 @@ private void spawnUiElements() {
sendButton = new Button("Send");
}

/**
* Sets the parameters of the ui elements.
*/
private void setUiSettings() {
scrollPane.setPrefSize(385, 535);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
Expand All @@ -101,6 +107,10 @@ private void setUiSettings() {
sendButton.setPrefWidth(55.0);
dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0));
}

/**
* Sets the anchorpane settings.
*/
private void setAnchorPaneSettings() {
AnchorPane.setTopAnchor(scrollPane, 1.0);
AnchorPane.setBottomAnchor(sendButton, 1.0);
Expand All @@ -109,6 +119,9 @@ private void setAnchorPaneSettings() {
AnchorPane.setBottomAnchor(userInput, 1.0);
}

/**
* Adds any ui functionalities and interactables.
*/
private void addUiFunctionality() {
sendButton.setOnMouseClicked((event) -> input());
userInput.setOnAction((event) -> input());
Expand Down

0 comments on commit 16efbff

Please sign in to comment.