I'm looking for suggestions on how to go about validating input from a user. My assignment is to execute commands based on a textual input from the user. My only concern is that there can be many variations of commands that are acceptable.
For example these commands are all acceptable and do the same thing, "show the game board"
sh board,
sho board,
show board,
show bo,
sho bo,
sh bo
There are about 10 other commands that share this similar property so I was wondering what would be the best practice of going about validating a users input?
Should I store all the different combinations in a hashmap?
Look into regex (regular expressions). These are great for when you want to use values that are not necessarily complete.
For example:
Say I type "shutdo"
With regex you can make your program understand that anything after the string "shutd" means to powerOff()
It looks like the minimum command allowed length is 2.
So first you check if the length of the term is at least 2.
Next, you can loop over the available commands,
and stop at the first that starts with the term, for example:
List<String> commands = Arrays.asList("show", "create", "delete");
for (String command : commands) {
if (command.startsWith(term)) {
// found a match, command is: command
break;
}
}
If the commands are very specific and limited, I would just add all of them into some data structure (hash being one of them).
If the problem was that you're supposed to understand what the user input is supposed to do, then I would say find the pattern using either regex or a simple pattern validation (looks like they're all two words, first starting with "sh" and second starting with "bo").
But honestly, ~15 commands aren't that big of deal in terms of space/efficiency.
Edit:
There are about 10 other commands that share this similar property
If this means 10 more commands like "show board", then I would say store it in hash. But if I misunderstood you and you mean that there are 10 other commands that do similar things ("set piece", "set pie", "se pi", etc), then RegEx is the way to go.
If I understood you correctly, there are N distinct commands, which can be combined. It shall be allowed to abbreviate each command as long it stays unambiguous.
If this is the case, the following methods expandCommands(String) and expandCommand(String) will normalize each command part.
public class Main {
static Set<String> availableCommands = new HashSet<>(Arrays.asList(
"show",
"board",
"btest"
));
public static void main(String[] args) throws Exception {
List<String> testData = Arrays.asList(
"sh board",
"sho board",
"show board",
"show bo",
"sho bo",
"sh bo"
);
String expected = "show board";
for (String test : testData) {
String actual = expandCommands(test);
if (!expected.equals(actual)) {
System.out.println(test + "\t"+ actual);
}
}
try {
expandCommands("sh b");
throw new IllegalStateException();
} catch (Exception e) {
if (!"not unique command: b".equals(e.getMessage())) {
throw new Exception();
}
}
try {
expandCommands("sh asd");
throw new IllegalStateException();
} catch (Exception e) {
if (!"unknown command: asd".equals(e.getMessage())) {
throw new Exception();
}
}
}
private static String expandCommands(String aInput) throws Exception {
final String[] commandParts = aInput.split("\\s+");
StringBuilder result = new StringBuilder();
for (String commandPart : commandParts) {
String command = expandCommand(commandPart);
result.append(command).append(" ");
}
return result.toString().trim();
}
private static String expandCommand(final String aCommandPart) throws Exception {
String match = null;
for (String candidate : availableCommands) {
if (candidate.startsWith(aCommandPart)) {
if (match != null) {
throw new Exception("not unique command: " + aCommandPart);
}
match = candidate;
}
}
if (match == null) {
throw new Exception("unknown command: " + aCommandPart);
}
return match;
}
}
The Set<String> availableCommands contains all possible commands.
Every part of the input command is checked, if it is the start of exactly one available command.
You can use reg-ex matching to validate input. E.g., the pattern below will match anything that starts with sh followed by 0 or more characters, then a space and then bo followed by 0 or more chars.
public class Validator {
public static void main (String[] args) {
String pattern = "sh[\\w]* bo[\\w]*";
System.out.println(args[0].matches(pattern));
}
}
Related
I'm creating a text-based RPG for my AP computer Science final Project and I want to make it so that at any point the player can type "help" and it will display a message in the console. The only way I can think to do it would be to use the Scanner import and do something like:
String help = scan.nextLine();
switch(help) {
case help:
System.out.println("help message");
break;
}
I've been teaching myself Java for the project I'm very new and I'm aware this is probably an extremely inefficient method for this, not to mention it would only work at 1 point. So if someone could point me in the right direction I'd be eternally grateful.
Also: I have searched for the answer before submitting this post but I couldn't find one that described how to print it to the console throughout the game.
You could create a method that handles asking for help, which you will always use instead of scan.nextLine():
public static String getNextLine(Scanner scan) {
String str = scan.nextLine(); //Get the next line
if (str.equals("help") { //Check whether the input is "help"
System.out.println(/* help */); //if the input is "help", print your help text
return getNextLine(scan); //try again.
else {
return str; //return the inputted string
}
}
Now, wherever you would use scan.nextLine, use getNextLine(scan), instead. This way, you will automatically account for when the user inputs "help". (Just a tip, you might want to use equalsIgnoreCase instead of equals so that if the user types, say, "Help", your code still works.)
Use a giant if..elif..then statement to capture commands, not a switch statement. Do not forget to use the String.equals() method to compare strings, too! See why here.
String cmd = scan.nextLine();
if(cmd.equals("help")) {
System.out.println("help message");
} else if(cmd.equals("move")) {
System.out.println("move");
} else {
System.out.println("I'm sorry, I did not understand that command :(")
}
I'd use the Apache Commons CommandLine class in addition to the HelperFormatter:
private static final String HELP = "help";
private static final String SOME_OTHER_TOGGLE = "toggle";
/**
* See --help for command line options.
*/
public static void main(String[] args) throws ParseException {
Options options = buildOptions();
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse(options, args);
if (line.hasOption(HELP)) {
printHelp(options);
}
}
private static void printHelp(Options options) {
String header = "Do something useful with an input file\n\n";
String footer = "\nPlease report issues at http://example.com/issues";
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("myapp", header, options, footer, true);
}
private static Options buildOptions() {
Options options = new Options();
options.addOption(OptionBuilder
.withLongOpt(SOME_OTHER_TOGGLE)
.withDescription("toggle the foo widget in the bar way")
.create()
);
options.addOption(OptionBuilder
.withLongOpt(HELP)
.withDescription("show the help")
.create()
);
// ... more options ...
return options;
}
I want to make a translator ex: English to Spanish.
I want to translate a large text with a map for the translation.
HashMap <String, Object> hashmap = new HashMap <String, Object>();
hashmap.put("hello", "holla");
.
.
.
Witch object should I use to handle my inital text of 1000 words? A String or StringBuilder is fine ?
How can I do a large replace? Without iterate each word with each element of the map ?
I don't want take each word of the string, and see there is a match in my map
Maybe a multimap with the first letter of the word?
If you have any answer or advise thank you
Here is an example implementation:
import java.io.*;
import java.util.*;
public class Translator {
public enum Language {
EN, ES
}
private static final String TRANSLATION_TEMPLATE = "translation_%s_%s.properties";
private final Properties translations = new Properties();
public Translator(Language from, Language to) {
String translationFile = String.format(TRANSLATION_TEMPLATE, from, to);
try (InputStream is = getClass().getResourceAsStream(translationFile)) {
translations.load(is);
} catch (final IOException e) {
throw new RuntimeException("Could not read: " + translationFile, e);
}
}
private String[] translate(String text) {
String[] source = normalizeText(text);
List<String> translation = new ArrayList<>();
for (String sourceWord : source) {
translation.add(translateWord(sourceWord));
}
return translation.toArray(new String[source.length]);
}
private String translateWord(String sourceWord) {
Object value = translations.get(sourceWord);
String translatedWord;
if (value != null) {
translatedWord = String.valueOf(value);
}
else {
// if no translation is found, add the source word with a question mark
translatedWord = sourceWord + "?";
}
return translatedWord;
}
private String[] normalizeText(String text) {
String alphaText = text.replaceAll("[^A-Za-z]", " ");
return alphaText.split("\\s+");
}
public static void main(final String[] args) {
final Translator translator = new Translator(Language.EN, Language.ES);
System.out.println(Arrays.toString(translator.translate("hello world!")));
}
}
And put a file called 'translation_EN_ES.properties' on your classpath (e.g. src/main/resources) with:
hello=holla
world=mundo
If you know all the words before hand you could easily create a Regex Trie.
Then at runtime, compile the regex once. Then you are good to go.
To create the regex, download and install RegexFormat 5 here.
From the main menu, select Tools -> Strings to Regex - Ternary Tree
paste the list in the input box, then press the Generate button.
It spits out a full regex Trie that is as fast as any hash lookup there is.
Copy the compressed output from that dialog into Rxform tab (mdi) window.
Right click window to get the Context menu, select Misc Utilities -> Line Wrap
set it for about a 60 character width, press ok.
Next press the C++ button from the windows toolbar to bring up the MegaString
dialog. Click make C-style strings Lines Catenated-1 press OK.
Copy and paste the result into your Java source.
Use the regex in a Replace-All with callback.
In the callback use the match as a key into your hash table to return the
translation to replace.
Its simple, one pass and oh so fast.
For a more extreme example of the tool see this regex of a 130,000 word dictionary.
Sample of the letter X
"(?:x(?:anth(?:a(?:m|n|te(?:s)?)|e(?:in|ne)|i(?:an|"
"c|n(?:e)?|um)|o(?:ma(?:s|ta)?|psia|us|xyl))|e(?:be"
"c(?:s)?|n(?:arthral|i(?:a(?:l)?|um)|o(?:biotic|cry"
"st(?:s)?|g(?:amy|enous|raft(?:s)?)|lith(?:s)?|mani"
"a|n|ph(?:ile(?:s)?|ob(?:e(?:s)?|ia|y)|ya)|time))|r"
"(?:a(?:fin(?:s)?|n(?:sis|tic)|rch|sia)|ic|o(?:derm"
"(?:a|i(?:a|c))|graphy|m(?:a(?:s|ta)?|orph(?:s)?)|p"
"h(?:agy|ily|yt(?:e(?:s)?|ic))|s(?:is|tom(?:a|ia))|"
"t(?:es|ic))))|i(?:pho(?:id(?:al)?|pag(?:ic|us)|sur"
"an))?|oan(?:a|on)|u|y(?:l(?:e(?:m|n(?:e(?:s)?|ol(?"
":s)?))|i(?:c|tol)|o(?:carp(?:s)?|g(?:en(?:ous)?|ra"
"ph(?:s|y)?)|id(?:in)?|l(?:ogy|s)?|m(?:a(?:s)?|eter"
"(?:s)?)|nic|ph(?:ag(?:an|e(?:s)?)|on(?:e(?:s)?|ic)"
")|rimba(?:s)?|se|tomous)|yl(?:s)?)|st(?:er(?:s)?|i"
"|o(?:i|s)|s|us)?)))"
Just kicking the tires of Java 8, and noticed Lamdas and streams functional programing.
Was wondering if a simple command line args consumer could use streams.
Cant figure out howto get at two stream elements at once however...
Plus below I'd need to handle args that do and dont take values, so couldnt do any odd/even trickery. Would have consider args to always start with a dash, and optional values never do.
String[] args = ("-v", "-c", "myconfigfile", "-o", "outputfile");
Arrays.toList(args).stream().map( a,v -> evalArg(a,v));
public static void evalArg(String arg, String val) {
switch(arg) {
case "-v":
verbose = true;
break;
case "-c":
config_file = val;
break;
case "-o":
output_file = val;
break;
default:
System.err.println("unknown argument " + arg + " " + val);
break;
}
}
If you have key-value pairs then you can use following:
public static void main(final String[] args) {
String[] args = {"-v", "value", "-c", "myconfigfile", "-o", "outputfile"};
pairStream(Arrays.asList(args), (param, value) -> param + ": " + value)
.forEach(System.out::println);
}
public static <X, Y> Stream<Y> pairStream(List<X> list, BiFunction<X, X, Y> mapper) {
Supplier<X> s = list.iterator()::next;
return Stream.generate(() -> mapper.apply(s.get(), s.get()))
.limit(list.size() / 2);
}
// Result:
// -v: value
// -c: myconfigfile
// -o: outputfile
Stream API is not really built to process values which depent on the state of another value. The used lambda function should be stateless (see also the Java documentation: Stream#map()). However it is possible to use sequential() on the stream to ensure the correct order and allow using a state inside the function used to process the elements, but that is not really recommended.
Better use a library for parameter parsing like Apache Commons CLI.
After writing my original answer I realised this is possible using reduce, for example like this:
String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).reduce((arg, val) -> {
switch (arg) {
case "-v":
verbose = true;
break;
case "-c":
configFile = val;
break;
case "-o":
outputFile = val;
break;
// Non-exhaustive
}
return val;
});
My original answer, using state and function objects:
Remember that Java does not actually support first class functions. When you are using a lambda, you're actually passing an object containing a function. These objects can have state. You can use this to your advantage, so you should be able to do something like this:
String[] foo = {"-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
String last;
public void accept(String t) {
if (last == null) {
last = t;
} else {
System.out.println(last + " " + t);
last = null;
}
}
});
Whether this is a good idea is a different consideration though. Note the use of forEachOrdered as plain old forEach is not guaranteed to go through the list in a specific order. Also note that map would not work for doing one thing with two elements, as a mapping function has to take one argument and return one result, resulting in a one-to-one relationship between the input stream and the output stream.
For your specific example you would have to do something like this though:
String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
String arg;
public void accept(String val) {
if (arg == null) {
arg = val;
} else if (t.startsWith("-")) {
System.out.println(arg);
arg = val;
} else {
System.out.println(arg + " " + val);
arg = null;
}
}
});
A third alternative is of course to do as yshavit suggests and not use streams at all:
LinkedList<String> argList = LinkedList<>(Arrays.asList(args));
while(!argList.isEmpty()) {
switch (argList.pop()) {
case "-v":
verbose = true;
break;
case "-c":
configFile = argList.pop();
break;
case "-o":
outputFile = argList.pop();
break;
default:
System.err.println("unknown argument " + arg + " " + val);
break;
}
}
There's no easy way to do this with lambdas/mapping. Not all forms of iteration lend themselves to mapping a single lambda.
For instance, the functional approach to this probably wouldn't use lambdas; it would use recursion, with each step optionally popping more args if it needs to. Something like (in pseudo-code, and ignoring error checking, etc):
Options[] parse(args : String[]):
if args.isEmpty:
return []
currentOpt, remainingArgs = args.pop()
case currentOpt of:
"-v":
return [ Verbose ] ++ parse(remainingArgs)
"-c":
configPath, remainingArgs = remainingArgs.pop()
return [ Config(configPath) ] ++ parse(remainingArgs)
"-o":
outputPath, remainingArgs = remainingArgs.pop()
return [ Output(outputPath) ] ++ parse(remainingArgs)
_:
return [ UnknownOption(currentOpt) ] ++ parse(remainingArgs)
You seem to have the idea, but I thought I would expand on it with a little additional data.
The concept of a data stream is probably older than Java; processing them goes back at least to processor architectures. CPUs are built with something called Von Neumann Architecture; which allows the processor to maintain pointers to different parts of memory and, basically, have access to all of it all the time. Some other processors in common use (such as GPUs) are stream processors. They handle one operation at a time, and outside of an occasional (and often dubious) clever trick, have no knowledge of any other part of the stream. This allows for optimal parallel processing, among other things, and is precisely why GPGPU can be blisteringly effective for some tasks, but is woefully ineffective at running an entire machine.
The Streams API allows you to run operations under the same circumstances. The only way to operate on more than one item at once is through a reduce; which is basically the construction of another stream. Through it, in theory, you can certainly scan arguments and launch options; but due to the potential redundancy, that doesn't mean it's the best in practice.
I have a question regarding structuring of code.
I have let us say three types of packages A,B and C.
Now, classes in package A contains classes which contain the main() function. These classes
need some command line arguments to run.
In package B, there are classes which contains some public variables, which need to be configured, at different times. For example before calling function A, the variable should be set or reset, the output differs according to this variable.
In package C, uses the classes in package B to perform some tasks. They do configure their variables as said before. Not only when the object is created, but also at intermediate stage.
Package A also has classes which in turn use classes from package B and package C. In order to configure the variables in classes of B and C, class in package A containing the main() function, reads command line arguments and passes the correct values to respective class.
Now, given this scenario, I want to use Apache Commons CLI parser.
I am unable to understand how exactly I should write my code to be structured in an elegant way. What is a good design practice for such scenario.
Initially I wrote a class without Apache to parse the command line arguments.
Since I want a suggestion on design issue, I will give an excerpt of code rather than complete code.
public class ProcessArgs
{
private String optionA= "default";
private String optionB= "default";
private String optionC= "default";
public void printHelp ()
{
System.out.println ("FLAG : DESCRIPTION : DEFAULT VALUE");
System.out.println ("-A <Option A> : Enable Option A : " + optionA);
System.out.println ("-B <Option B> : Enable Option B : " + optionB);
System.out.println ("-C <Option C> : Enable Option C : " + optionC);
}
public void printConfig()
{
System.out.println ("Option A " + optionA);
System.out.println ("Option B " + optionB);
System.out.println ("Option C " + optionC);
}
public void parseArgs (String[] args)
{
for (int i=0;i<args.length;i++)
{
if (args[i].equalsIgnoreCase ("-A"))
optionA = args[++i];
else if (args[i].equalsIgnoreCase ("-B"))
optionB = args[++i];
else if (args[i].equalsIgnoreCase ("-C"))
optionC = args[++i];
else
throw new RuntimeException ("Wrong Argument : " + args[i] + " :: -h for Help.");
}
}
}
Points to note -
I already have 50+ command line options and they are all in one place.
Every class uses only a group of command line options.
I tried to write an interface, somehow but I am unsuccessful. I am not sure if this is a good way to do it or not. I need some design guidelines.
Here is the code which I wrote -
public interface ClassOptions
{
Options getClassOptions();
void setClassOptions(Options options);
}
public class Aclass implements ClassOptions
{
private String optionA="defaultA";
private String optionB="defaultB";
public Options getClassOptions()
{
Options options = new Options();
options.addOption("A", true, "Enable Option A");
options.addOption("B", true, "Enable Option B");
return options;
}
public void setClassOptions(Options options, String args[])
{
CommandLineParser parser = new BasicParser();
CommandLine cmd=null;
try
{
cmd = parser.parse( options, args);
} catch (ParseException e)
{
// TODO Auto-generated catch block
// e.printStackTrace();
System.out.println("ignored option");
}
if(cmd.hasOption("A"))
optionA = "enabled";
if(cmd.hasOption("B"))
optionB = "enabled";
}
}
I think the problems in such writing of code are -
There are different types of arguments like int, double, string, boolean. How to handle them all.
getClassOption() and setClassOption() both contain the arguments "A", "B" for example. This code is prone to errors made while writing code, which I would like to eliminate.
I think the code is getting repetitive here, which could be encapsulated somehow in another class.
Not all the arguments are required, but can be ignored.
Thank You !
I would recommend to you JCommander.
I think it's a really good Argument Parser for Java.
You define all the Argument stuff within annotations and just call JCommander to parse it.
On top of that it also (based on your annotations) can print out the corresponding help page.
You don't have to take care about anything.
I believe you will love it! :)
Take a look at it: http://jcommander.org/
There are a lot of examples and such!
Good Luck! :)
simple example for command line argument
class CMDLineArgument
{
public static void main(String args[])
{
int length=args.length();
String array[]=new String[length];
for(int i=0;i<length;i++)
{
array[i]=args[i];
}
for(int i=0;i<length;i++)
{
System.out.println(array[i]);
}
I am trying to get JLine to do tab completion so I can enter something like the following:
commandname --arg1 value1 --arg2 value2
I am using the following code:
final List<Completor> completors = Arrays.asList(
new SimpleCompletor("commandname "),
new SimpleCompletor("--arg1"),
new SimpleCompletor("--arg2"),
new NullCompletor());
consoleReader.addCompletor(new ArgumentCompletor(completors));
But after I type the value2 tab completion stops.
(Suplementary question, can I validate value1 as a date using jline?)
I had the same problem, and I solved it by creating my own classes to complete the commands with jLine. I just needed to implement my own Completor.
I am developing an application that could assist DBAs to type not only the command names, but also the parameters. I am using jLine for just for the Terminal interactions, and I created another Completor.
I have to provide the complete grammar to the Completor, and that is the objective of my application. It is called Zemucan and it is hosted in SourceForge; this application is initially focused to DB2, but any grammar could be incorporated. The example of the Completor I am using is:
public final int complete(final String buffer, final int cursor,
#SuppressWarnings("rawtypes") final List candidateRaw) {
final List<String> candidates = candidateRaw;
final String phrase = buffer.substring(0, cursor);
try {
// Analyzes the typed phrase. This is my program: Zemucan.
// ReturnOptions is an object that contains the possible options of the command.
// It can propose complete the command name, or propose options.
final ReturnOptions answer = InterfaceCore.analyzePhrase(phrase);
// The first candidate is the new phrase.
final String complete = answer.getPhrase().toLowerCase();
// Deletes extra spaces.
final String trim = phrase.trim().toLowerCase();
// Compares if they are equal.
if (complete.startsWith(trim)) {
// Takes the difference.
String diff = complete.substring(trim.length());
if (diff.startsWith(" ") && phrase.endsWith(" ")) {
diff = diff.substring(1, diff.length());
}
candidates.add(diff);
} else {
candidates.add("");
}
// There are options or phrases, then add them as
// candidates. There is not a predefined phrase.
candidates.addAll(this.fromArrayToColletion(answer.getPhrases()));
candidates.addAll(this.fromArrayToColletion(answer.getOptions()));
// Adds a dummy option, in order to prevent that
// jLine adds automatically the option as a phrase.
if ((candidates.size() == 2) && (answer.getOptions().length == 1)
&& (answer.getPhrases().length == 0)) {
candidates.add("");
}
} catch (final AbstractZemucanException e) {
String cause = "";
if (e.getCause() != null) {
cause = e.getCause().toString();
}
if (e.getCause() != null) {
final Throwable ex = e.getCause();
}
System.exit(InputReader.ASSISTING_ERROR);
}
return cursor;
This is an extract of the application. You could do a simple Completor, and you have to provide an array of options. Eventually, you will want to implement your own CompletionHandler to improve the way that the options are presented to the user.
The complete code is available here.
Create 2 completors, then use them to complete arbituary arguments. Note that not all the arguments need to be completed.
List<Completer> completors = new LinkedList<>();
// Completes using the filesystem
completors.add(new FileNameCompleter());
// Completes using random words
completors.add(new StringsCompleter("--arg0", "--arg1", "command"));
// Aggregate the above completors
AggregateCompleter aggComp = new AggregateCompleter(completors);
// Parse the buffer line and complete each token
ArgumentCompleter argComp = new ArgumentCompleter(aggComp);
// Don't require all completors to match
argComp.setStrict(false);
// Add it all together
conReader.addCompleter(argComp);
Remove the NullCompletor and you will have what you want. NullCompletor makes sure your entire command is only 3 words long.