So im making an application that works via console commands (think of "database new db_name"). Whats the most elegant way to handle this sort of thing?
Heres my little prototype, basicly just tons of switches. I mean it works but it gets kinda messy once I want to add stuff like putting out all the parameters if you pass a wrong one or too few, so im wondering if theres a better option:
public static void main(String[] args) {
while (running) {
Scanner in = new Scanner(System.in);
String cmd = in.nextLine();
parseCmd(cmd);
}
}
private static void parseCmd(String s) {
String[] cmd = s.split(" ");
try {
switch (cmd[0]) {
case "exit":
running = false;
System.out.println("Shutting down...");
break;
case "database":
switch (cmd[1]) {
case "new":
databases.add(new Database(cmd[2]));
break;
default:
System.out.println("Unknown parameter");
break;
}
break;
default:
System.out.println("Unknown command");
break;
}
} catch (Exception ArrayIndexOutOfBoundsException) {
System.out.println("Too few arguments passed");
}
}
I don't know about the most elegant way, but I've done something like this a couple of times and came up with this solution.
Make a class for "database" that represents the command, once you find the string "database", you retrieve an instance of this class and pass it the remaining arguments, so it can do the rest of the parsing.
class DataBaseCommand implements Command {
#Override
public void eval(String[] args) {...}
}
You can then keep a static map of these commands mapped to their names:
private static final Map<String, Command> commands = new HashMap<>();
static {
commands.put("database", new DataBaseCommand());
}
switch (cmd[0]) {
case "exit":
running = false;
System.out.println("Shutting down...");
break;
default:
if(commands.contains(cmd[0])) {
commands.get(cmd[0]).eval(cmd);
} else {
System.out.println("Unknown command");
}
break;
}
Maybe you could use a library that does a lot of those command parsing for you, like joptsimple?
I would suggest using http://jcommander.org/.Its simple and elegant. In it you can use inbuilt annotations
e.g Create a class like this using jcommander annotaions
#Parameters(commandNames = {"test", "Manages dates"})
public class CommandOption {
#Parameter(names = {"-e", "--exit"}, description = "bla bla")
public boolean exit = false;
}
then in main method
public static void main(String... args){
CommandOption opt = new CommandOption();
JCommander jc = new JCommander(opt);
jc.parse(args);
//and now use
System.out.println("value" +opt.exit)
}
and then can pass parmaeters as java class-e true or maybe better if you can create jar and then java -jar 'jar' -e true
Related
I'm coding something like cmd in java, so I made a few commands like (hello world), , etc. All the commands are classes that implement the interface (I named it as "API"), but to identify which command is written, I use the "if" expression, so to add the new command (class), I have to write this expression every time, for instance:
if (command.equals("print")){return new Print();}
Well, that's the question, how can I write an expression once to make java identify a new command (class), no matter how much I add?
Thanks in advance;
That's the code:
This interface helps the program identify which command is written:
public interface UI {
API get();
Function<String, UI> check = command -> {
if (command.equals("print")){return new Print();}
if (command.equals("change")){return new Change();}
if (command.equals("exit")){return new Exit();}
return new NotFound();
};
static UI of(String command){
return check.apply(command);
}
}
An example of a class:
public class Print extends Backend implements API, UI{
#Override
public boolean command() {
System.out.println(string);
return true;
}
#Override
public API get() {
return new Print();
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
boolean run = true;
while (run) {
System.out.print("Java> ");
//UI
UI command = UI.of(scanner.next());
API com = command.get();
run = com.command();
}
}
}
I have a switch method, and I want that if the type of fetch is ALL, the switch runs in all the statments, (HOUSE AND PLAYER), but it doesn't work because of the break, it only runs in HOUSE, removing the break works, but then if the type is not ALL, it will also runs on the others statments and not only in the given type. Whats is a proper solution for that?
public static void fetch(FetchType fetchType) {
switch (updateType) {
case ALL: // if this is the case, then it should run for HOUSE, PLAYER
case HOUSE:
// code here
break;
case PLAYER:
// code here
break;
default:
throw new IllegalArgumentException();
}
}
Well you can either create private methods for that:
public static void fetch(FetchType fetchType) {
switch (updateType) {
case ALL:
doHouse();
doPlayer();
break;
case HOUSE:
doHouse();
break;
case PLAYER:
doPlayer();
break;
default:
throw new IllegalArgumentException();
}
}
private static void doHouse(){ ... }
private static void doPlayer(){ ... }
Or use Runnable to avoid using polluting the local private namespace:
public static void fetch(FetchType fetchType) {
Runnable house = () -> { ... };
Runnable player = () -> { ... };
switch (updateType) {
case ALL:
house.run();
player.run();
break;
case HOUSE:
house.run();
break;
case PLAYER:
player.run();
break;
default:
throw new IllegalArgumentException();
}
}
If I understand it right you might need to create methods for each case in the Main class and then call all methods if the fetch type is "ALL"
static void methodForHouse() {
System.out.println("I just got executed!");
}
static void methodForPlayer() {
System.out.println("I just got executed!");
}
And then
public static void fetch(FetchType fetchType) {
switch (updateType) {
case ALL:
methodForHouse();
methodForPlayer();
break;
case HOUSE:
methodForHouse();
break;
case PLAYER:
methodForPlayer();
break;
default:
throw new IllegalArgumentException();
}
}
I'm trying to create a console to handle console commands from a string.
At the moment I'm using if statements to check for each command like this:
if (command.contains("new train")) {
command = command.replace("new train ", "");
Train t = new Train();
t.setCode(command);
ServiceProvider.getTrainService().saveOrUpdate(t);
responeHandler("train " + command + " created");
}
But this isn't the best solution in my opinion.
I'm wondering whether there is already a good design pattern for problems like this?
I have looked at the builder and factory patterns but can't really decide if they are the right choice.
A Command and Factory pattern maybe?
interface Command {
void execute();
}
interface CommandFactory {
boolean canCreate(String input);
Command fromInput(String input); // or return Optional so it can be a FunctionalInterface
}
class TrainCommand implements Command {
String train;
public TrainCommand(String t) { train = t; }
public void execute() {
ServiceProvider.getTrainService().saveOrUpdate(t);
}
}
class TrainCommandFactory {
public boolean canCreate(String t) {
return t.contains("new train ");
}
public Command fromString(String c) {
return new TrainCommand(c.replace("new train ", ""));
}
}
And a Singleton Composite CommandFactory that iterates all known Command Factories:
class CommandFactories implements CommandFactory {
private static final CommandFactories INSTANCE;
private List<CommandFactory> delegates = Arrays.asList(
new TrainCommandFactory()
// others
};
public boolean canCreate(String t) {
return delegates.stream()
.filter(cf -> cf.canCreate(t))
.findAny().isPresent();
}
public Command fromString(String c) {
return delegates.stream()
.filter(cf -> cf.canCreate(t))
.findAny()
.map(CommandFactory::fromString);
}
}
You could store your commands in an array and when the user enters a command, you could find the item having the given index. The index would be meaningful and usable in a switch-case and if the item is not found, you could give a meaningful response.
Also, you could do this in a case-insensitive manner of having the keys in the array all lower characters and turning the command key to lower before the search:
protected String[] supportedCommands = {"first", "second", "third"};
public static int findCommand(String command) {
for (var i = 0; i < supportedCommands.length; i++) {
if (command.equals(supportedCommands[i])) return i;
}
return -1; //Not found
}
public static void handleCommand(String command) {
int c = findCommand(command.toLowerCase());
switch (c) {
case 1: {/*...*/} break;
default: {/*Handle Undefined command*/}
}
}
I think using defined commands is the proper way. An important issue for a command would be to be identified (matches) by the commandString and to be executed (execute). Once you have created custom Command you can register them in a List and execute them.
interface Command{
boolean matches(String commandString);
boolean execute(String commandString);
}
an Example implementation would be
CreateTrainCommand implements Command{
private final CMDSTRING = "new train";
#Override
public boolean matches(CommandString cmdStr){
if(cmdStr != null && cmdStr.toLowerCase.startsWith(CMDSTRING)){
return true;
}
return false;
}
#Override
public boolean matches(CommandString cmdStr){
if(cmdStr != null){
String train = cmdString.toLowerCase.replace(CMDSTRING, "").trim();
//here comes your command execution
Train t = new Train();
...
}
return true;//execution succesful
}
}
if you want to use these command store all commands into a List (or any other collection) and check if your command matches your input
List<Command> cmds = ...
cmds.add(new CreateTrainCommand()); //add all commands that were implemented
//or only some commands if the user has restricted access
here is how you apply the commands
String commandString = ... //from Scanner or where else
for(Command cmd: cmds){ //use streams if you're java 8
if (cmd.matches(commandString)){
boolean wasSuccesful = cmd.execute(commandString);
break;
}
}
A Map<String, Consumer<String>> could do the job to associate commands to actions.
It is not the GOF factory and command DP.
But these are fair and simple implementations of factory and command pattern.
So you should consider it too.
Map<String, Consumer<String>> actionsByCommand = new HashMap<>();
actionsByCommand.put("new train", command->{
command = command.replace("new train ", "");
Train t = new Train();
t.setCode(command);
ServiceProvider.getTrainService().saveOrUpdate(t);
responeHandler("train " + command + " created");
});
actionsByCommand.put("delete train", command->{
command = command.replace("delete train ", "");
...
});
// and so for...
You could also create a special action for invalid commands that you don't need to put in the map :
Consumer<String> invalidCommandAction = (command-> System.out.println("Invalid command. Here are the accepted commands..."));
To improve the testability and the maintainability of action classes, you could
move them into distinct classes.
Map<String, Consumer<String>> actionsByCommand = new HashMap<>();
actionsByCommand.put("new train", new NewTrainCommand());
actionsByCommand.put("delete train", new DeleteTrainCommand());
With NewTrainAction defined as :
public class NewTrainAction implements Consumer<String>{
public void accept(String command){
command = command.replace("new train ", "");
Train t = new Train();
t.setCode(command);
ServiceProvider.getTrainService().saveOrUpdate(t);
responeHandler("train " + command + " created");
}
}
And other Actions defined in the same way.
Then you can use them in this way :
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String command = scanner.nextLine();
Consumer<String> action = actionsByCommand.getOrDefault(command, invalidCommandAction);
action.accept(command);
}
If you are in the Spring world you can use
You could consider to implement
org.springframework.boot.CommandLineRunner
Each command could be executed in its own CommandLineRunne instance.
Use
org.springframework.core.env.SimpleCommandLinePropertySource
to parse your command line
So currently, I have some code, that is extremely messy and does the opposite of following dry rules. This is killing me internally, and I would love to fix it, if I knew how.
I have a class called Commands, this have two enums in it. Subs, and Options. What I would like to do, is make a method, such as this one here:
public void makeTab(String args, List<String> command, Commands type) {
if (args.equals("")) {
for (Commands.type commd : Commands.type.values()) {
command.add(commd.name().toLowerCase());
}
} else {
for (Commands.type commd : Commands.type.values()) {
if (commd.name().toLowerCase().startsWith(args)) {
command.add(commd.name().toLowerCase());
}
}
}
}
Then, if that method actually worked like I wanted it to, I could then do this.
List<String> command = new ArrayList<>();
switch (args.length) {
case 1:
makeTab(args[0], command, Subs);
break;
case 2:
makeTab(args[1], command, Options);
break;
}
Very sadly though, as you should be able to tell, this doesn't work, specifically because of the "Commands type" bit in the method. The problem is, I don't know what to put there to work, I've tried "Class type", "enum type", "Enum type". So, because of this catastrophe, my code is currently looking like this.
List<String> comd = new ArrayList<>();
switch (args.length) {
case 1:
if (args[0].equals("")) {
for (Commands.Subs commd : Commands.Subs.values()) {
comd.add(commd.name().toLowerCase());
}
} else {
for (Commands.Subs commd : Commands.Subs.values()) {
if (commd.name().toLowerCase().startsWith(args[0])) {
comd.add(commd.name().toLowerCase());
}
}
}
break;
case 2:
if (args[1].equals("")) {
for (Commands.Options commd : Commands.Options.values()) {
comd.add(commd.name().toLowerCase());
}
} else {
for (Commands.Options commd : Commands.Options.values()) {
if (commd.name().toLowerCase().startsWith(args[1])) {
comd.add(commd.name().toLowerCase());
}
}
}
break;
}
tl;dr I'm trying to make the last code block cleaner by making a method for the if, else.
Rather than taking values yourself, have the caller pass values() to you:
public void makeTab(String args, List<String> command, Enum[] values) {
for (Enum commd : values) {
String lowerName = commd.name().toLowerCase()
if (lowerName.startsWith(args)) {
command.add(lowerName);
}
}
}
The caller would invoke your method as follows:
makeTab(args, command, Options.values());
makeTab(args, command, Subs.values());
Note that there is no need to check args to be an empty string, because when args is empty, startsWith(args) returns true for any String value.
Hi I have wrapper application and a jar which runs in a service of wrapper app. In the complete solution the Jar is my product which I can't revile to users, but I need to give them the liberty to extend the functionality by registering function against a command received from socket. They can do it in the wrapper application. I already have some commands coming from flex UI and they are processed like following:
private void processCommand(String tempCommand) throws NumberFormatException, IOException, ELearningException
{
ApplicationLog.log("Command Queue " + tempCommand, true);
String[] commandParameters = tempCommand.split("#");
switch (Integer.parseInt(commandParameters[0]))
{
case CONSTANTS.INITIALIZE:
if (this.m_isInitialized)
break;
else
{
InitializeTeacherJar.instantiate(tempCommand.split("#")[1], this.baseContext, tempCommand.split("#")[2]);
parent = InitializeTeacherJar.getInstance();
parent.setMyFlexServer(this.m_localServerSocket);
parent.setMyFlexSocket(this.m_localSocket);
this.m_isInitialized = true;
}
break;
case CONSTANTS.LOGIN:
/**
* Store student details in hash map
*/
this.writeToStudentJava(tempCommand, JavaServerThreadForTeacher.getIpAddress().getHostAddress());
if(tempCommand.split("#")[1].equalsIgnoreCase(CONSTANTS.SUCCESS))
{
HashMap<String, ArrayList<String>> temp = parent.getStudentIPList();
ArrayList<String> value= new ArrayList<String>();
value.add(tempCommand.split("#")[3]);
value.add("present");
temp.put(tempCommand.split("#")[2], value);
parent.setStudentIPList(temp);
if (StudentUtility.studentCounter < 0)
StudentUtility.studentCounter = 0;
StudentUtility.studentCounter = StudentUtility.studentCounter + 1;
parent.getMyFlexSocket().getOutputStream().write((CONSTANTS.PING + parent.getDelimiter() + StudentUtility.studentCounter).getBytes());
System.out.print("StudentUtility.studentCounter :: "+StudentUtility.studentCounter);
}
break;
case CONSTANTS.COURSE:
parent.setCourse(tempCommand.split(parent.getDelimiter())[1]);
break;
case CONSTANTS.ACTION:
parent.performAction(tempCommand, commandParameters[3]);
parent.getMyFlexSocket().getOutputStream().write((CONSTANTS.PING + parent.getDelimiter() + StudentUtility.studentCounter).getBytes());
break;
case CONSTANTS.INTERACTIVE:
if (commandParameters[1].equalsIgnoreCase(CONSTANTS.Play)) {
parent.playAudio(commandParameters[2], true);
} else if (commandParameters[1].equalsIgnoreCase(CONSTANTS.Record)) {
parent.startAudioRecording(commandParameters[2], true);
} else {
parent.playAudio(commandParameters[2], false);
}
case CONSTANTS.TTL:
this.m_isWifiConnected();
break;
case CONSTANTS.DELETE:
parent.deleteFile(commandParameters[1], true);
// deleteRecording(commandParameters[3],
// commandParameters[1]
// + commandParameters[2]);
// deleteEveryThing(Environment.getExternalStorageDirectory()
// .getAbsolutePath() + "/" + commandParameters[2]);
// deleteEveryThing(pathToSave + "/Response/" + course + "/"
// + commandParameters[1]);
break;
case CONSTANTS.RESTART:
StudentUtility.sendToEveryStudent(tempCommand);
StudentUtility.displayOnProjector(tempCommand);
parent.setStudentIPList(new HashMap<String, ArrayList<String>>());
parent.setCourse("");
break;
case CONSTANTS.EXIT:
StudentUtility.displayOnProjector(tempCommand);
StudentUtility.sendToEveryStudent(tempCommand);
break;
default:
break;
}
}
This is inside my Jar and I want users to be able to add any number of cases as functions in wrapper application. More over, preferably, I would like to put the existing cases in the same hash map used for registering commands and functions in wrapper application.
I need to know the correct design pattern and some advice how to go about it.
Thanks in advance.
Combine design pattern Command with a mapping of function code to commands. This has the benefit of moving the implementations to their own self-contained classes, which makes code more manageable and easier to test.
You might want to use Design Pattern Visitor instead of modifying the command map, but depends on your circumstances.
A common Interface for your commands
public Interface Command {
void action();
}
Create a bunch of concrete commands
public class Course implements Command {
private final String tempCommand;
private final ParentClass parent;
public Course(final String tempCommand, final ParentClass parent) {
this.tempCommand = tempCommand;
this.parent = parent;
}
public void action() {
parent.setCourse(tempCommand.split(parent.getDelimiter())[1]);
}
};
Map the preexisting function codes to commands.
private final Map<Integer, Command> getBuiltIns() {
Private final Map<Integer, Command> cmdMap = new HashMap<Integer, Command>();
cmdMap.add(CONSTANTS.COURSE, new Command(tempCommand, parent));
<etc>
return cmdMap;
}
private final cmdMap = getBuildIns();
Make a method that can add commands
public void addCommand(final int code, final Command command) {
// Check if code is already used, or there might be trouble
cmdMap.add(code, command);
}
Find the command then execute.
private void processCommand(String tempCommand) throws NumberFormatException, IOException, ELearningException
{
final Command theCommand = cmdMap.get(Integer.parseInt(commandParameters[0]));
theCommand.action();
}