|
||||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
See:
Description
Interface Summary | |
CommandListener | This interface must be implemented by classes that intend to respond to command-line arguments processed by the CommandParser. |
Class Summary | |
AbstractCommandListener | This class is intended to more easily support the implementation of command-line argument handler classes by providing an empty optionMatched method. |
CommandOption | This class provides support for defining command-line arguments. |
CommandParser | This class provides support for parsing command-line arguments. |
DefaultCommandListener | This class allows boilerplate nested classes to be elimitated by providing all of the things supplied by the CommandListener interface as arguments to the constructor. |
DelimitedCommandOption | This class provides support for multi-valued options which are specified using delmited values. |
JoinedCommandOption | This class provides support for "joined" command options. |
MutexOptionConstraint | This class provides an implementation of a mutual exclusion constraint for two options. |
OptionConstraint | This is the base class for all option constraints. |
PosixCommandOption | This class provides support for POSIX-compliant command options. |
RepeatableCommandOption | This class provides the basic support for repeatable command line arguments. |
RequiredOptionConstraint | This class provides an implementation of a constraint which requires the specific option to be matched. |
RequiresAnyOptionConstraint | This class provides an implementation of a dependency constraint between options. |
Provides classes to assist in easily creating command-line Java applications. The implementation is roughly based on the Red Hat popt library, but is not intended to be compatible with this API.
There are basically two ways to use this package. The first approach is probably most familiar to users of the Red Hat popt packages and C programmers used to processing command line arguments in general, while the second approach applies Object Oriented Programming principles to handling the arguments. With either approach, there is complete support for accessing additional or "left over" arguments which are supplied on the command line but which do not explicitly apply to a single option.
With this usage model, the application wishing to
respond to command line arguments simply defines instances
of the arguments it wishes to recognize and then registers
itself as a CommandListener
with the appropriate
CommandParser
instance. After this is done, most of the work of dealing
with the application takes place in the CommandListener
's
implementation of optionMatched
method. While there is nothing wrong with this approach (it
will be very familiar to anyone who has used the popt or
similar libraries), it can easily result in a very
large if
statement if the application supports
many long options or many case
's of a
switch
statement if primarily short options are
used.
import com.townleyenterprises.command.*; public class cltest implements CommandListener { static CommandOption[] opts = { new CommandOption("foo", 'f', false, null, "isn't this foo?"), new CommandOption("bar", 'b', true, "<name>", "specifies the bar"), new CommandOption("only-long", (char)0, true, "<opt>", "this option only has a long form") }; public CommandOption[] getOptions() { return opts; } public void optionMatched(CommandOption opt, String arg) { switch(opt.getShortName().charValue()) { case 'f': System.out.println("matched f"); break; case 'b': System.out.println("matched bar: " + arg); break; } } public String getName() { return "cltest options"; } public static void main(String[] args) { CommandParser clp = new CommandParser("cltest"); clp.addCommandListener(new cltest()); clp.parse(args); String[] largs = clp.getUnhandledArguments(); for(int i = 0; i < largs.length; ++i) { System.out.println("largs[" + i + "] = '" + largs[i] + "'"); } } }
If the command-line parsing needs of an application are modest, this approach is easy to understand and straightforward to implement quickly. However, applications which have more complex command-line argument handling needs might be better off using the object-oriented approach.
Since this library does not intend to be compatible with
existing command-line parsing libraries and since Java is an
object-oriented language, it makes sense to also provide an
OO approach to the command-line argument problem. Rather
than separating the behavior of what should happen when each
option is matched from the option itself, the normal
operations of detecting if the option has been matched and
capturing the option argument is encapsulated into the
CommandOption
class.
The ComandParser
calls the
optionMatched
method of each option it
matches before it notifies all of the registered listeners. In the
default case, the method simply sets a boolean value to
indicate if the option has been matched and captures the
argument for later retrieval. If the option is
declared as a named object rather than as an anonymous
object in the CommandObject
array returned from
the listener, the application can check if the option has
been matched by simply asking the option. There is no need
to provide any code in the listener's
optionMatched
method. This approach is
illustrated in Example 2.
import com.townleyenterprises.command.*; public class cltest implements CommandListener { static CommandOption doa = new CommandOption("aop", 'a', false, null, "The 'a' operation should be performed"); static CommandOption dob = new CommandOption("bop", 'b', false, null, "The 'b' operation should be performed"); static CommandOption doc = new CommandOption("cop", 'c', false, null, "The 'c' operation should be performed"); static CommandOption[] opts = { doa, dob, doc }; public CommandOption[] getOptions() { return opts; } public void optionMatched(CommandOption opt, String arg) { // nothing to do } public String getDescription() { return "cltest options"; } public static void main(String[] args) { CommandParser clp = new CommandParser("cltest"); clp.addCommandListener(new cltest()); clp.parse(args); if(doa.getMatched()) { System.out.println("Supposed to do a"); } if(dob.getMatched()) { System.out.println("Supposed to do b"); } if(doc.getMatched()) { System.out.println("Supposed to do c"); } String[] largs = clp.getUnhandledArguments(); for(int i = 0; i < largs.length; ++i) { System.out.println("largs[" + i + "] = '" + largs[i] + "'"); } } }
The above example might not seem to be a large improvement
over the Classic Mode at first glance. The main advantage
in this case is that the application is going to do
something more complicated than print some text if any of
the options, or any combination of the options, have been
matched by the command parser. Using the Classic Mode or in
some other command-line parsing libraries, boolean values
would have to be set in the listener's optionMatched
method. This approach partially approximates the flag
concept present in the popt library.
A variation on Example 2 may be used by applications which
want to perform all argument parsing and then perform the
requested actions rather than potentially performing only
some of the requested commands. This approach also does not
require the listener to implement a body to the
optionMatched
method. Each argument must be
named and is responsible for capturing the arguments for
later use. This technique can be implemented simply by
making minor modifications to Example
2 as shown below:
import com.townleyenterprises.command.*; public class cltest implements CommandListener { static CommandOption doa = new CommandOption("aop", 'a', true, "<arg>", "The 'a' operation should be performed"); static CommandOption dob = new CommandOption("bop", 'b', true, "<arg>", "The 'b' operation should be performed"); static CommandOption doc = new CommandOption("cop", 'c', false, null, "The 'c' operation should be performed"); static CommandOption[] opts = { doa, dob, doc }; public CommandOption[] getOptions() { return opts; } public void optionMatched(CommandOption opt, String arg) { // nothing to do } public String getDescription() { return "cltest options"; } public static void main(String[] args) { CommandParser clp = new CommandParser("cltest"); clp.addCommandListener(new cltest()); clp.parse(args); if(doa.getMatched()) { System.out.println("Supposed to do a using '" + doa.getArg() + "'"); } if(dob.getMatched()) { System.out.println("Supposed to do b using '" + dob.getArg() + "'"); } if(doc.getMatched()) { System.out.println("Supposed to do c"); } String[] largs = clp.getUnhandledArguments(); for(int i = 0; i < largs.length; ++i) { System.out.println("largs[" + i + "] = '" + largs[i] + "'"); } } }
The facilities described above should handle most of the command-line argument parsing needs of an application in a more efficient manner than is often required when using a non-OO approach. The specific parameters and switches which are often created within the application to determine if arguments have been matched have been replaced with allowing the options to track themselves, thus removing a lot of tedious programming overhead from the application developer.
However, to handle the remainder of the possible needs of
command line option parsing, it is possible to provide
additional behavior by deriving a new class from the
CommandOption
class and implementing additional
behavior in the optionMatched
method. The most
obvious examples of this type of need would be:
As an example, take a hypothetical Java program which works
on directories and files. The program can optionally exclude
any files or directories in the list of files to process by
using the --exclude
option. If the Classic
Model was used, the specific logic to handle multiple
instances of the option would be embedded in the
CommandListener
. However, the library provides a clean way
to implement this feature in a reusable way. Example 4
illustrates a way to implement this functionality.
import java.util.*; import com.townleyenterprises.command.*; class RepeatableOption extends CommandOption { RepeatableOption(String longName, char shortName, String argHelp, String argDesc) { super(longName, shortName, true, argHelp, argDesc); } public void optionMatched(String arg) { super.optionMatched(arg); _args.add(arg); } List getArgs() { return _args; } ArrayList _args = new ArrayList(); } public class feather implements CommandListener { static CommandOption create = new CommandOption("create", 'c', false, null, "Create a new archive."); static CommandOption file = new CommandOption("file", 'f', true, "<filename>", "Specify the name of the archive" + " (default is stdout)."); static RepeatableOption xclude = new RepeatableOption("exclude", 'X', "[ <filename> | <directory> ]", "Exclude the named file or directory" + " from the archive"); static CommandOption[] opts = { create, file, xclude }; public CommandOption[] getOptions() { return opts; } public void optionMatched(CommandOption opt, String arg) { // nothing to do } public String getDescription() { return "feather options"; } public static void main(String[] args) { CommandParser clp = new CommandParser("feather", "FILE..."); clp.addCommandListener(new feather()); clp.parse(args); if((file.getMatched() || xclude.getMatched()) && !create.getMatched()) { System.err.println("error: nothing to do"); clp.usage(); System.exit(-1); } String[] largs = clp.getUnhandledArguments(); if(create.getMatched() && largs.length == 0) { System.err.println("error: refusing " + "to create empty archive."); clp.usage(); System.exit(-2); } for(int i = 0; i < largs.length; ++i) { System.out.println("largs[" + i + "] = '" + largs[i] + "'"); } if(xclude.getMatched()) { System.out.println("Excluded:"); List xas = xclude.getArgs(); for(Iterator j = xas.iterator(); j.hasNext();) { System.out.println(j.next()); } } } }
The above code will correctly process the following command line:
$ java feather -X /tmp -X /var -c -f out.feather /and print the following output:
largs[0] = '/' Excluded: /tmp /var
The library is designed to make parsing command lines easy and to eliminate the tedious nature of doing so, much in the same way that the javax.swing.Action class streamlined the handling of GUI events. However, the application developer ultimately is the one making the decision about how the library best fits in with the way they want do develop the application. Both the Classic Model and the OO Model have been used in production code and both work equally well. The main difference is in how much extra, repetative code must be written to perform the same tasks.
At the present time, the following limitations/issues are present in the package:
-display -500 -cvzf
|
||||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |