Overcoming Positional Parameter Parsing in Java
This primer into positional parameter parsing using a custom class is a good reminder of how to keep your programs flexible.
Join the DZone community and get the full member experience.
Join For FreeDuring the initial days of learning any programming language, it is natural to hard code all the inputs needed by a program. But, as we learn more about a programming language, and also learn to create more flexible programs, there is a need to develop a 'generic' program. For the context of this article, I have defined a 'generic' application to be one that accepts command-line parameters.
I will cover three methods of handling parameters passed to an application.
Method 1: Positional Parameters
This method is the first step for handling parameters. The main method of Java accepts an array of strings, and it is only natural to access command line parameters using the array indices. Thus, if a program accepts two parameters, it would use the arguments given below:
. . .
public static void main(String[] args)
{
. . .
String inFilePath = args[0];
String outFilePath = args[1];
. . .
open file in read mode using inFilePath
open file in write mode using outFilePath
. . .
}
. . .
The biggest problem of this method is that the arguments parsed by the application are positional in nature. If, by any chance, the user makes the mistake of specifying the parameters incorrectly, the whole operation can end in disaster. For example, in the above example, if the paths are interchanged, we will end up overwriting an existing file! Surely not what we want from the application.
Method 2: Checking for Parameters
Instead of using positional parameters, can we not use a more robust and flexible method? Can we not use named arguments? In fact, we can — and the answer is simple. Here is an example:
. . .
public static void main(String[] args)
{
. . .
String inFilePath = null;
String outFilePath = null;
for ( int i = 0; i < args.length; i++ ) {
if ( args[i].equals("-i") ) {
i++;
inFilePath = args[i];
} else if ( args[i].equals("-o") ) {
i++;
outFilePath = args[i];
} else {
// nothing to do, the unrecognized argument will be skipped
}
}
open file in read mode using inFilePath
open file in write mode using outFilePath
. . .
}
. . .
To invoke the application, we use the following method:
java -jar copy.jar CopyFile -i infile.txt -o outFile.txt
Method 3: Custom Class
While the code shown in method two is simple, it is tedious to check each parameter in an explicit manner. Can we not create another mechanism? Indeed we can. This the very same thing, but using a custom class for this purpose. The class for command line parsing is as below:
import java.util.ArrayList;
@SuppressWarnings("unchecked")
public class CommandOptions
{
protected ArrayList arguments;
public CommandOptions(String[] args)
{
parse(args);
}
public void parse(String[] args)
{
arguments = new ArrayList();
for ( int i = 0; i < args.length; i++ ) {
arguments.add(args[i]);
}
}
public int size()
{
return arguments.size();
}
public boolean hasOption(String option)
{
boolean hasValue = false;
String str;
for ( int i = 0; i < arguments.size(); i++ ) {
str = (String)arguments.get(i);
if ( true == str.equalsIgnoreCase(option) ) {
hasValue = true;
break;
}
}
return hasValue;
}
public String valueOf(String option)
{
String value = null;
String str;
for ( int i = 0; i < arguments.size(); i++ ) {
str = (String)arguments.get(i);
if ( true == str.equalsIgnoreCase(option) ) {
value = (String)arguments.get(i+1);
break;
}
}
return value;
}
}
. . .
public static void main(String[] args)
{
CommandOptions cmd = new CommandOptions(args);
String inFilePath = null;
String outFilePath = null;
if ( cmd.hasOption("-i") ) {
inFilePath = cmd.valueOf("-i");
} else if ( cmd.hasOption("-o") ) {
outFilePath = cmd.valueOf("-o");
}
open file in read mode using inFilePath
open file in write mode using outFilePath
. . .
}
. . .
To invoke the application, we use the following method:
java -jar copy.jar CopyFile -i infile.txt -o outFile.txt
Conclusion
The way I have presented the methods may leave you feeling that these are a gradual and evolutionary progression. In fact, after living with Method 1 for quite some time, I created the custom class described in Method 3 — and also published it as 'public domain' code on code.google.com. Unfortunately, the site closed down. It was only recently that I created Method 3 for an application where I did not want to go through the elaborate mechanism of a custom class.
Opinions expressed by DZone contributors are their own.
Comments