I've a nice desgin pattern for doing stuff like this, which does not involve any library and is very simple to achieve.
Steps:
1. Create an enum class like below using regex to parse the commands.
2. Create a main-loop, which is reading from the console parsing the command to that enum from 1. over and over.
3. Use a switch-case to dispatch the command to the right method.
ad 1.) The enum-class where you have all your commands. You could also externalize those strings if you want.
For the rawCommand it should be immutable. Better would be, creating a class which contains that rawCommand and the enum as fields.
package issue.system.core;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import issue.system.model.IssueState;
/**
* Command enumeration containing plenty of useful methods to parse commands
* @author Alessandro Giusa
* @version 1.0
*
*/
public enum Command {
INIT("^init$"),
ISSUE("^issue #([A-Za-z0-9_])+$"),
OPEN("^open #([A-Za-z0-9_])+$"),
SHOW("^show(( #([A-Za-z0-9_])+( ([0-9])+)?)|( #([A-Za-z0-9_])+ tags)|( t([A-Za-z0-9_])+)(,t([A-Za-z0-9_])+)*)?( -s[0-9](-[0-9]+)?)?( -f (and|or))?$"),
REMOVE("^remove(( #([A-Za-z0-9_])+( t([A-Za-z0-9_])+)?)|(( t([A-Za-z0-9_])+)(,t([A-Za-z0-9_])+)* (-f (and|or)))|( released))$"),
RENAME("^rename( #([A-Za-z0-9_])+ #([A-Za-z0-9_])+)|(( t([A-Za-z0-9_])+ t([A-Za-z0-9_])+))$"),
STATE("^state #([A-Za-z0-9_])+ (" + IssueState.getRegexForCommand() + "$"),
HELP("^help$"),
QUIT("(^quit)|(^q)$"),
QUIT_AND_COMMIT("(^qc)$"),
TAG("^tag(( #([A-Za-z0-9_])+( t([A-Za-z0-9_])+)(,t([A-Za-z0-9_])+)*)|(( t([A-Za-z0-9_])+)(,t([A-Za-z0-9_])+)*)|( -r( t([A-Za-z0-9_])+)(,t([A-Za-z0-9_])+)*))?$"),
CLEAR("^clear$"),
GIT("^git( commit)?$"),
PATCH_NOTES("^patchnotes$")
;
public static final String DELIMITER_COMMAND_TOKEN_COMMAND_TEXT = " ";
private final String regex;
private String rawCommand;
private Command(final String regex) {
this.regex = regex;
}
public String getRegex() {
return this.regex;
}
public String getRawCommand() {
return this.rawCommand;
}
public void setRawCommand(final String rawCommand) {
this.rawCommand = rawCommand;
}
/**
* Parse the given raw string into a command if there is one corresponding to
* @param rawString
* @return option to command
*/
public static Optional<Command> parseCommand(final String rawString) {
Matcher matcher;
for (final Command nextCommand : values()) {
matcher = Pattern.compile(nextCommand.getRegex()).matcher(rawString);
if (matcher.find()) {
final String rawCommand = rawString.substring(
rawString.indexOf(DELIMITER_COMMAND_TOKEN_COMMAND_TEXT, 0)
+ 1, rawString.length());
nextCommand.setRawCommand(rawCommand);
return Optional.of(nextCommand);
}
}
return Optional.empty();
}
}
This is a class I use in my command line issue system.
ad 2.)
The main loop which is reading from command line over and over. The Terminal class is just a convenient class for sys-out stuff.
@Override
public void run() {
this.running = true;
while (this.running) {
final String line = Terminal.readLine(CURSOR);
if(line == null) {
Terminal.println("cmd line was empty");
continue;
}
final Optional<Command> optCommand = Command.parseCommand(line);
if (optCommand.isPresent()) {
if (optCommand.get() == Command.INIT || this.loadIssueMetadata(optCommand.get())) {
this.processCommand(optCommand.get());
}
} else {
Terminal.printf("the given command %s is not a vaild. Use <help> to see all commands.", line);
}
}
System.exit(0);
}
ad 3.) And the switch-case part where you dispatch the command to the right method to process.
void processCommand(final Command command) {
switch (command) {
case INIT:
this.init();
break;
case ISSUE:
this.issue(command.getRawCommand());
break;
case SHOW:
this.show(command.getRawCommand());
break;
case QUIT:
this.quit();
break;
case STATE:
this.state(command.getRawCommand());
break;
case REMOVE:
this.remove(command.getRawCommand());
break;
case RENAME:
this.rename(command.getRawCommand());
break;
case HELP:
usage();
break;
case TAG:
this.tag(command.getRawCommand());
break;
case OPEN:
this.open(command.getRawCommand());
break;
case CLEAR:
this.cls();
break;
case GIT:
this.git(command.getRawCommand());
break;
case QUIT_AND_COMMIT:
this.quitAndCommit();
break;
case PATCH_NOTES:
this.patchNotes();
break;
default:
break;
}
}
I hope you got the idea. If you are more interested let me know!
Good programming!