Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Java Command-Line Arguments


FEB96: Java Command-Line Arguments

Greg, a programmer at I/NET, is currently working on the Web Server/400 product, a commercially available Web server for AS/400 computers. He can be contacted at [email protected].


Users of the first release of the Web server my company developed made one request more often than any other: "Please include printed documentation." In the first release, documentation consisted of more than 160 HTML files, viewable from any browser supporting HTML 2.0. While users could load documents into a browser and print them, all links ended up getting lost--not to mention that the printing sequence wasn't clear. For our part, we also needed a formatted text version of some of the documentation for reference as comments within configuration files.

While trying to solve this problem, I ran across RtfToHtml, a program that converts rich text format (RTF) documents to HTML (see ftp://ftp.cray.com/src/WWWstuff/RTF/rtftohtml_overview.html). Unfortunately, our source documentation was already in HTML, so we would have had to convert it to RTF and manually insert the styles that RtfToHtml can handle. Instead, I wrote a program that reads in HTML and writes out RTF. By the time I was done, I had written a system that has, at its core, a Java application that converts an HTML file to an RTF file (with links preserved as bookmarks) or text file. The name of the application (and its package of classes) is HtmlXlate.

In this article, I'll introduce CmdLnArg, a package of Java classes which parses the command-line parameters for the application. In future articles, I'll detail the process of converting HTML to RTF and formatting text. This article and the accompanying code are based on the 1.0 beta of the Java development kit (JDK).

The Java Programming Language

Developed by Sun Microsystems, Java is an object-oriented language similar to C++. The language has garnered attention because its portability, security, and support for distributed programming make it useful for Internet programming. To find out more about Java, refer to the articles "Java and Internet Programming," by Arthur van Hoff (DDJ, August 1995), "Net Gets a Java Buzz," by Ray Valdes (Dr. Dobb's Developer Update, August 1995), and "Programming HotJava Applets," by John Rodley (Dr. Dobb's Web Sourcebook, November/December 1995). You can also find Java-related information at http://java.sun.com.

There are differences between Java and C++ in both the language and the environment. Java does not include familiar C++ features such as pointers, preprocessing, multiple inheritance, goto, and automatic coercion. While I might have liked to keep some of these features, Sun makes a case for their elimination, and the net effect is good. Sun has also added features, such as automatic garbage collection.

Most of the interest in Java currently centers around "applets." Applets are like programs, but are loaded and run by a Java-enabled browser such as HotJava or Netscape Navigator 2.0 beta. A normal HTML document can contain a reference to an applet that causes the applet to be loaded and run (similar to the way images are referenced). Applets are given an area of the screen to work with and tend (but don't have) to be oriented towards graphics and GUI.

Java applications, like applets, are written in the Java language, but are loaded and run directly by the Java interpreter rather than by a browser. These are usually command-line programs, but don't have to be. (In the Alpha3 release of the JDK, the HotJava browser was implemented as a Java application. At this writing, there is no HotJava browser available for the Beta 1.0 release of the JDK.) I chose to make HtmlXlate an application instead of an applet because I didn't need to display graphics. It is also useful to be able to call the application from within other command-line programs (a make program, for example).

When developing in Java, you must compile the source, but you don't have to explicitly link it. It will be dynamically linked and loaded at run time by the Java interpreter. The interpreter also checks the code it is loading for errors and virus-like behavior. The code can then either be interpreted or converted to the machine code for the current platform. This delayed (optional) conversion to machine code allows any machine and operating system with a Java interpreter to run the application without recompiling.

A Java "package" provides a way to collect a related group of classes. Within the package, each class can be public or not. Only classes declared public can be used outside of a package. When I originally wrote HtmlXlate, I put the classes that process command-line arguments into the same package as the rest of the code. Afterwards, I put those classes into a separate package (CmdLnArg) for easier maintenance and reuse.

Sun defines conventions for command-line arguments in the documenthttp://java.sun.com/progGuide/java/cmdLineArgs/conventions.html. The document describes "word" arguments, "arguments that require arguments," and "flags."

  • Word arguments are full words that turn an option on or off; for example, a typical word argument is -verbose.
  • Arguments that require arguments consist of a keyword followed by additional information (such as what format to output). In HtmlXlate, for example, the keyword is -to and the argument is RtfWriter, TextWriter, or CommentWriter.
  • Flags are like word arguments except that they have only one letter and can be combined (for example, -vg would be two separate flags--v and g). I didn't use flags in HtmlXlate.
HtmlXlate requires support for word arguments and for arguments that require arguments. All arguments should have a leading hyphen. If more than one flag argument is specified on the command line, they can be combined behind one hyphen.

The Test Program

At this point, I'm going to assume that you have the beta version of the JDK installed. If not, you can get it at http://java.sun.com/. Although I've tested this code only with the Windows 95 JDK, it should work with other implementations as well.

To start, pick a directory that will contain your Java-related files. You will need to modify your CLASSPATH environment variable to include this drive and directory. Below the directory you just created, create the subdirectory CmdLnArg (the same name as the package), which will contain the Java source files presented in Listings One through Four.

The directory in which you place the source file must have the same name as the package, or the Java compiler won't know where to look for the classes when you use them from outside the package. (At first, I gave them different names, which resulted in strange compiler errors.)

To compile the source, enter the commands in Example 1. (Note that the same case must be used for the filenames entered on the command line.)

Of the classes being compiled, all but the last (Go) are part of the CmdLnArg package. The Go class is a test application that uses the package and prints the results to STDOUT. To test the program, type java Go -in file.in -out file.out. This starts the Java interpreter (java.exe) and tells it to run the Go application (see Listing Five). The rest of the parameters are passed into the Go class for interpretation. To see all the parameters Go supports, run the application without parameters: java Go. (Go is not a reserved name, just the name of the class I picked to include a static main method.)

Using the Package

To use the package in your applications, follow the model provided in the processArgs method of the Go application. All applications must follow the same basic steps:

    1. Create a new instance of ArgProcessor and pass it the arguments the application received from the interpreter; see Example 2(a).

    2. Create and add ArgReqArgType and WordArgType instances for each argument you want to process; see Examples 2(b) and 2(c). Example 2(c) uses a shortcut: ArgReqArgType takes a StringBuffer as input during construction. Because a StringBuffer is a modifiable object, the original StringBuffer (inputName) can be modified in step 3. This means that your code does not need to maintain a reference to the ArgReqArgType object, but can just check the StringBuffer object for the results later.

    3. Call the ArgProcessor.process method to update the ArgReqArgType and WordArgType objects added in step 2; see Example 2(d).

    4. Interpret the results. For both WordArgType and ArgReqArgType, the result is stored in a public member named argDest. For WordArgType, argDest is a Boolean; for ArgReqArgType, it is a StringBuffer. Note that for ArgReqArgTypes, you can also reference the StringBuffer used to create the ArgReqArgType (it is the same object that argDest references); see Examples 2(e) and 2(f).

How the Package Works

The package can be divided into the ArgProcessor class (Listing One) and the argument type classes that it processes (Listings Two through Four).

The ArgProcessor class maintains a Vector of argument types (vArgs) passed in through the add method. Vector is a class in the package java.util and is described as a "growable array." When the process method is called, each argument passed into the application is compared to the argument types stored in vArgs (via a call to getArg). If an argument type is found, it will be set through a call to its set method.

The getArg method makes use of the Enumeration interface, also in the java.util package. Java interfaces are meant to provide the benefits of multiple inheritance without the complications. An interface is a type that specifies methods and constants, but does not specify how the methods will be implemented. The Enumeration interface, for example, specifies the hasMoreElements and nextElements methods. It is up to the class implementing the interface to implement those methods (in this case, VectorEnumerator, found in Vector.java, which is shipped with the JDK).

The use of the Enumeration interface and Vector class illustrates these features of the Java API, but I probably should have used the Dictionary class instead. This would have made my code simpler and probably faster; I know more now about the Java API than I did when I started.

GenericArgType (Listing Two) is an abstract class that provides little implementation, but defines a method that ArgReqArgType and WordArgType must implement. The abstract keyword in the class declaration means that it is not possible to instantiate an object of this type, only a nonabstract subclass. (Even though a GenericArgType object cannot be instantiated, variables can be declared to be GenericArgType if they reference objects that subclass GenericArgType.)

The constructor in WordArgType (Listing Three) shows how a superclass constructor can be called. The super(argID) line is a call to GenericArgType's constructor. The set method will toggle the Boolean argDest the first time it is called and do nothing if called after that. This stops argDest from flipping back and forth if the user inadvertently specifies the argument more than once on the command line.

The constructor for ArgReqArgType (Listing Four) is not much different than the constructor for WordArgType. The set method is more complicated because it needs to use the command-line argument after the one containing the leading hyphen. If the user has specified an argument requiring an argument and the required argument is missing, an exception will be thrown. Otherwise, argDest will be set, and the index will be incremented by one.

Conclusion

One enhancement to the package would be the addition of support for flag arguments. This would not be too difficult and would probably require another Vector class in ArgProcessor to keep track of the flag arguments separately. This would allow a check first for word arguments and arguments that require arguments. If that check failed, then a check for flag arguments would be made.

Another logical enhancement for this package might be to warn the user when the arguments are not valid. This could be done in the process method of the ArgProcessor class by adding an Else clause at the bottom of the While loop.

I encourage you to download the JDK and work through a few small example programs on your own. Java is relatively easy to pick up, especially if you are familiar with C++. Java also comes with an API that includes enough functionality to let you concentrate on the interesting parts of your program. You may also want to watch my page at http://www.inetmi.com/~gwhi since I plan on putting up interesting Java code and information as time permits.

Example 1: Enter these commands to compile Java source.

javac GenericArgType.java
javac WordArgType.java
javac ArgReqArgType.java
javac ArgProcessor.java
javac Go.java

Example 2: Using the CmdLnArg package.

(a)
ArgProcessor processor = new ArgProcessor(args)

(b)
WordArgType verboseArg = new WordArgType("-verbose", false);


processor.add(verboseArg);

(c)
StringBuffer inputName = new StringBuffer("");


processor.add(new ArgReqArgType("-in", inputName);

(d)
processor.process();

(e)
boolean bVerbose = verboseArg.argDest;

(f)
String s = inputName.toString();<B></B>

Listing One

// -- Declare this source to be a part of the CmdLnArg package. 
package CmdLnArg;
// -- Make use of shipped package for Vector, String, StringBuffer classes
import java.util.*;
public class ArgProcessor
    {
    // -- Args as passed into application
    String args[] = null;
    // -- All arg types we will process. Must be derived from GenericArgType.
    Vector vArgs = new Vector();
    // -- Constructor. Hang on to the arguments for when we need to
    // -- process them later.
    public ArgProcessor
        (
        String args[]
        )
        {
        this.args = args;
        } // -- end constructor
    // -- Adds one argument type for when we process
    public void add
        (
        GenericArgType arg
        )
        {
        vArgs.addElement(arg);
        } // -- end add
    // -- Process the args that were passed into the application and store
    // -- the results back in the argument types passed into the add method
    public void process
        (
        )
        throws
            Exception
        {
        int iArgIndex = 0;
        // -- Loop through all of the args passed into the application
        while (iArgIndex < args.length)
            {
            String thisArg = args[iArgIndex++];
            GenericArgType arg = getArg(thisArg);
            // -- Set the argument and (possibly) increase the arg index.
            if (arg != null)
                iArgIndex = arg.set(args, iArgIndex);
            } // -- end while
        } // -- end process
    // -- Look for an argument type that matches the passed-in target
    protected GenericArgType getArg
        (
        String argTarget
        )
        {
        Enumeration allArgs  = vArgs.elements();
        GenericArgType foundArg = null;
        // -- Loop through each arg type until we find a match or there are
        // -- no more arg types.
        while (foundArg==null && allArgs.hasMoreElements())
            {
            GenericArgType testArg = (GenericArgType)allArgs.nextElement();
            if (testArg.argID.equalsIgnoreCase(argTarget))
                foundArg =  testArg;
            } // -- end while
        return foundArg;
        } // -- end getArg
    } // -- end class ArgProcessor

Listing Two

// -- Declare this source to be a part of the CmdLnArg package.
package CmdLnArg;
// -- Make use of shipped packages for Vector, String, StringBuffer classes
import java.util.*;
public abstract class GenericArgType
    {
    public String argID = null;
    protected boolean wasSet;
    public GenericArgType
        (
        String argID
        )
        {
        this.argID = argID;
        wasSet = false;
        } // -- end constructor
    public boolean wasSet
        (
        )
        {
        return wasSet;
        } // -- end wasSet
    public abstract int set
        (
        String args[],
        int    iThisIndex
        )
        throws
            Exception
        ;
    } // -- end GenericArgType

Listing Three

// -- Declare this source to be a part of the CmdLnArg package.
package CmdLnArg;
// -- Make use of shipped packages for Vector, String, StringBuffer classes
import java.util.*;
public class WordArgType extends GenericArgType
    {
    public boolean argDest;
    public WordArgType
        (
        String argID,
        boolean argDest
        )
        {
        super(argID);
        this.argDest = argDest;
        } // -- end constructor
    public int set
        (
        String args[],
        int    iThisIndex
        )
        {
        if (!wasSet)
            {
            argDest = !argDest;
            wasSet = true;
            } // -- end if
        return iThisIndex;
        } // -- end set
    } // -- end class WordArgType

Listing Four

// -- Declare this source to be a part of the CmdLnArg package.
package CmdLnArg;
// -- Make use of shipped packages for Vector, String, StringBuffer classes
import java.util.*;
public class ArgReqArgType extends GenericArgType
    {
    public StringBuffer argDest = null;
    public ArgReqArgType
        (
        String       argID,
        StringBuffer argDest
        )
        {
        super(argID);
        this.argDest = argDest;
        } // -- end constructor
    public int set
        (
        String args[],
        int    iThisIndex
        )
        throws
            Exception
        {
        if (args.length <= iThisIndex)
            throw new Exception("Command line param "
                                + argID
                                + " must be followed by a string");
        // -- We can't directly change the string, so truncate it and add
        // -- what we want.
        argDest.setLength(0);
        argDest.append(args[iThisIndex]);
        wasSet = true;
        return iThisIndex + 1;
        } // -- end set
    } // -- end class ArgReqArgType

Listing Five

// -- Declare this source to be a part of the CmdLnArg package.
import CmdLnArg.*;
// -- Make use of shipped packages for Vector, String, StringBuffer,
// -- System classes
import java.util.*;
import java.io.*;
class Go
    {
    // -- Set by cmd line arguments
    static boolean       bVerbose    = false;
    static StringBuffer  inputName   = new StringBuffer("");
    static StringBuffer  outputName  = new StringBuffer("");
    static StringBuffer  to          = new StringBuffer("TextWriter");
    static StringBuffer  sWidth      = new StringBuffer("");
    static StringBuffer  sTabSize    = new StringBuffer("");
    static Vector vArgs = new Vector();
    public static void main
        (
        String args[]
        )
        {
        int iReturn = 0;
        try {
            processArgs(args);
            } // -- end try
        catch (Exception e)
            {
            e.printStackTrace();
            iReturn = 1;
            } // -- end catch
        System.exit(iReturn);
        } // -- end main
    static void processArgs
        (
        String args[]
        )
        throws
            Exception
        {
        ArgProcessor processor = new ArgProcessor(args);
        add(processor, new ArgReqArgType("-in",  inputName));
        add(processor, new ArgReqArgType("-out",  outputName));
        add(processor, new ArgReqArgType("-to",  to));
        add(processor, new ArgReqArgType("-width",  sWidth));
        add(processor, new ArgReqArgType("-tabsize", sTabSize));
        WordArgType verboseArg = new WordArgType("-verbose", false);
        add(processor, verboseArg);
        WordArgType includeRefs = new WordArgType("-NoRefs", true);
        add(processor, includeRefs);
        WordArgType refSections = new WordArgType("-NoSections", true);
        add(processor, refSections);
        WordArgType refPage = new WordArgType("-NoPages", true);
        add(processor, refPage);
        WordArgType horzRule = new WordArgType("-HR", true);
        add(processor, horzRule);
        processor.process();
        checkSomethingSet();
        // -- Print the results back to the user.
        System.out.println("Input name:" + inputName);
        System.out.println("Output name:" + outputName);
        System.out.println("to:" + to);
        System.out.println("width:" + sWidth);
        System.out.println("tabsize:" + sTabSize);
        if (verboseArg.wasSet())
            System.out.print("Verbose was set to ");
        else
            System.out.print("Verbose left at the default of ");
        System.out.println(String.valueOf(verboseArg.argDest));
        if (includeRefs.wasSet())
            System.out.print("IncludeRefs was set to ");
        else
            System.out.print("IncludeRefs left at the default of ");
        System.out.println(String.valueOf(includeRefs.argDest));
        if (refSections.wasSet())
            System.out.print("RefSections was set to ");
        else
            System.out.print("RefSections left at the default of ");
        System.out.println(String.valueOf(refSections.argDest));
        if (refPage.wasSet())
            System.out.print("RefPages was set to ");
        else
            System.out.print("RefPages left at the default of ");
        System.out.println(String.valueOf(refPage.argDest));
        if (horzRule.wasSet())
            System.out.print("HR was set to ");
        else
            System.out.print("HR left at the default of ");
        System.out.println(String.valueOf(horzRule.argDest));
        } // -- end processArgs
    // -- Adds an argument type to the processor and to our vector so we can
    // -- later make sure somethine was set.
    protected static void add
        (
        ArgProcessor   processor,
        GenericArgType argType
        )
        {
        processor.add(argType);
        vArgs.addElement(argType);
        } // -- end add
    // -- Prints info and generates and exception of nothing is set
    protected static void checkSomethingSet
        (
        )
        throws
            Exception
        {
        Enumeration allArgs  = vArgs.elements();
        boolean bAnythingSet = false;
        while (bAnythingSet==false && allArgs.hasMoreElements())
            {
            GenericArgType thisArg = (GenericArgType)allArgs.nextElement();
            if (thisArg.wasSet())
                bAnythingSet = true;
            } // -- end while
        if (!bAnythingSet)
            {
            System.out.println("Valid arguments requiring arguments:");
            System.out.println("  -in");
            System.out.println("  -out");
            System.out.println("  -to");
            System.out.println("  -width");
            System.out.println("  -tabsize");
            System.out.println();
            System.out.println("Valid word arguments:");
            System.out.println("  -verbose");
            System.out.println("  -NoRefs");
            System.out.println("  -NoSections");
            System.out.println("  -NoPages");
            System.out.println("  -HR");
            throw new Exception("No valid command-line arguments found");
            } // -- end if
        } // -- end checkSomethingSet
    } // -- end class Go


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.