HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

"Parsing Command-Line Arguments"
Volume: 4 Issue: 4, p. 20

Download files assoicated with this article

	

Listing 1. The mycat example declaration of tokens
 
import psk.cmdline.*; 

public class Main 
{ 
  // Initialize the applicationSettings and 
  // the tokens objects 
  static ApplicationSettings sm_main = 
                 new ApplicationSettings(); 
  static BooleanToken 
    sm_verbose = new BooleanToken( 
                  "v",   // switch name 
                  "verbose turned on or off", // message 
                  "",    // environment variable 
                  TokenOptions.optSwitch, // options 
                  false);   // default value 
  static IntegerToken 
    sm_lines = new IntegerToken( 
                  "l", 
                  "Number of empty lines to insert", 
                  "NUM_OF_LINES", 
                  TokenOptions.optSwitch, 
                  0); 
  static StringToken 
    sm_files =   new StringToken( 
                   "", 
                   "Test cases", 
                   "", 
                   TokenOptions.optArgument| 
                   TokenOptions.optMultiple| 
                 TokenOptions.optRequired, 
                   ""); 
  // Add all the token objects to the 
  // ApplicationSettings object 
  static { 
    sm_main.addToken(sm_verbose); 
    sm_main.addToken(sm_lines); 
    sm_main.addToken(sm_files); 
  } 

Listing  2.  The mycat example main() function
 
public static void main (String[] args) 
  { 
    try { 
      sm_main.parseArgs(args); 
  
      // Let's see what we 've got 
      System.out.println(sm_verbose.getValue() ? 
"verbose" : "not verbose"); 
      System.out.println("lines: " + Integer.toString 
(sm_lines.getValue(0))); 
      for (int i = 0; i < sm_files.NumberOfValues(); 
i++) 
        System.out.println("tests: " + sm_files.getValue(i)); 
    } catch (Exception ex) { 
  
    } 
  } 

Listing  3.  Output of the mycat example when no arguments are passed
 
Error: missing required argument. Name: 

Usage: program [-v ] [-l <Integer>]  <String> ... 

        -v 'verbose turned on or off'  Default: 
false 
        -l 'Number of empty lines to insert' 
Environment: 
$NUM_OF_LINES Default 
: 0 
         'Test cases' 

Listing 4.  The StringIterator class
 
// Copyright (c) 1998 Panos Kougiouris All 
Rights Reserved 
package psk.cmdline; 

/* 
 * This is a utility class. It encapsulates an array 
 * and an index that points to the current object 
 * 
 * @version  1.0,11/01/1998 
 * @author Panos Kougiouris 
 */ 

public class StringArrayIterator 
{ 
 public StringArrayIterator(String[] aStrings) { 
  m_index = 0; 
  m_strings = aStrings; 
 } 
  
 public boolean EOF() { 
  return m_index >= m_strings.length; 
 } 
  
 public void moveNext() { 
  m_index++; 
 } 
  
 public String get() { 
  return m_strings[m_index]; 
 } 
  
 private String[] m_strings; 
 private int      m_index; 
} 

Listing 5. The Token abstract class
 
// Copyright (c) 1998 Panos Kougiouris All Rights 
Reserved 
package psk.cmdline; 

import java.io.*; 
import java.util.*; 

/* 
 * Each Token object encapsulates one command 
 * line argumet or switch. 
 * 
 * @version  1.0, 11/02/1998 
 * @author Panos Kougiouris 
 */ 

public abstract class Token 
{ 
 public String name() {return m_name;}; 
  
 public int NumberOfValues() { 
  return m_values.size(); 
 } 
  
    public String extendedName() { 
        if (isSwitch()) { 
            return "-" + name(); 
        } else { 
            return name(); 
        } 
    } 
  
 //----------------------------------------------- 
 // Subclasses should implement these 
 //----------------------------------------------- 
  
 public abstract Object toObject(String lexeme); 
  
 // All but bool (where merely the appearence of 
the flag 
 // signifies the existence) return true; 
 public boolean hasOneOrMoreArgs() {return true;}; 
  
 public abstract String type(); 
  
 // Also implement these two methods 
 // <Type> getValue(int i); 
 // <Type> getValue(); 
  
 protected Token( 
  String a_name, 
  String a_message, 
  String a_environment_variable, 
  int aTokenOptions  // of type TokenOptions 
  ) { 
  m_name = a_name; 
  m_message = a_message; 
  m_env_variable = a_environment_variable; 
  m_flags = aTokenOptions; 
  m_firstTime = true; 
  m_values = new Vector(1); 
 }; 
  
 //----------------------------------------------- 
 // These methods are used by the ApplicationSettings class 
 // Thast' why they are protected 
 //----------------------------------------------- 
  
 // If we match the switch 
 // we parse as many command line arguments as they 
apply to this 
 // switch and then return just before the next one 
we do not 
 // recognize 
 protected boolean ParseSwitch(StringArrayIterator 
cmdLineArgs) 
throws Exception { 
  if (this.isArgument()) return false; 
  if ((this.isUsed()) && (!this.allowsMultipleValues())) 
return 
false; 
  if (cmdLineArgs.get().substring(1).indexOf(name()) 
!= 0) return 
false; 

  // after the match what remains e.g. if we are -t and 
  // argument is -tom then rest == 'om' 
  String lexeme = cmdLineArgs.get().substring(1 + 
name().length()); 
  if (lexeme.length() == 0) { 
   // the "-t foo" or "-t" case 
   if (this.hasOneOrMoreArgs()) { 
    // move to the "foo" 
    cmdLineArgs.moveNext(); 
    if (!cmdLineArgs.EOF()) 
     lexeme = cmdLineArgs.get(); 
    else { 
     String str = new String("Argument expected for option "); 
     str += this.extendedName(); 
     throw new Exception(str); 
    } 
   } 
  } else { 
   // "-tfoo" case 
   if (!this.hasOneOrMoreArgs()) { 
    String str = new String("No Argument expected for option "); 
    str += this.name(); 
    throw new Exception(str); 
   } 
  } 
  
  this.AddValueFromLexeme(lexeme); 
  this.setUsed(); 

        /* 
         * If you comment out these lines then 
         * "-l 1 2 3" will be permitted. Now this should be 
         * "-l 1 -l 2 -l 3" 
         * 
        if (allowsMultipleValues()) { 
      cmdLineArgs.moveNext(); 
      // if it supports multiple parse more arguments 
      while ((!cmdLineArgs.EOF()) && 
          (!isASwitch(cmdLineArgs.get()))) { 
       this.AddValueFromLexeme(cmdLineArgs.get()); 
       cmdLineArgs.moveNext(); 
      } 
      cmdLineArgs.movePrevious(); 
        } 
        */ 
  
  return true; 
 } 
  
 protected boolean parseArgument(StringArrayIterator 
cmdLineArgs) { 
  if (isSwitch()) return false; 
  if ((isUsed()) && 
   (!allowsMultipleValues())) return false; 
  
  // if it supports multiple parse more arguments 
  while ((!cmdLineArgs.EOF()) && 
      (!isASwitch(cmdLineArgs.get()))) { 
   this.AddValueFromLexeme(cmdLineArgs.get()); 
   this.setUsed(); 
   cmdLineArgs.moveNext(); 
   if (!allowsMultipleValues()) break; 
  } 
  
  return true; 
 } 
  
 protected void printUsage(java.io.PrintStream str) { 
  if (!this.isRequired()) str.print( "["); 
  str.print(this.extendedName() + " "); 
  str.print(this.type()); 
  if (this.allowsMultipleValues()) str.print(" ..."); 
  if (!this.isRequired()) str.print( "]"); 
  str.print(" "); 
 } 
  
 protected void printUsageExtended(java.io.PrintStream 
str) { 
  str.print("\t"); 
  str.print(this.extendedName() + " "); 
  str.print("'" + this.m_message + "' "); 
  if (hasEnvironmentVariable()) { 
   str.print(" Environment: $" + this.m_env_variable); 
  } 
  if (!this.isRequired()) { 
   str.print(" Default: "); 
   str.print(this.getDefaultValue()); 
  } 
  str.println(); 
 } 
  
 protected boolean hasEnvironmentVariable() 
 { 
  return this.m_env_variable.compareTo("") != 0; 
 } 
  
 protected String getEnvironmentVariable() { 
  return this.m_env_variable; 
 } 
  
 protected boolean isRequired() { 
  return (m_flags & TokenOptions.optRequired) == 
TokenOptions.optRequired; 
 }; 
  
 protected boolean isSwitch() { 
  return !isArgument(); 
 }; 
  
 protected boolean isArgument() { 
  return (m_flags & TokenOptions.optArgument) == 
TokenOptions.optArgument; 
 }; 
  
 protected boolean allowsMultipleValues() { 
  return (m_flags & TokenOptions.optMultiple) == 
TokenOptions.optMultiple; 
 }; 
  
 protected boolean isUsed() { 
  return (m_flags & TokenOptions.optAlreadyUsed) == 
TokenOptions.optAlreadyUsed; 
 }; 
  
 protected void setUsed() {m_flags |= 
TokenOptions.optAlreadyUsed;}; 
  
 protected void AddValueFromLexeme(String lexeme) { 
  if (m_firstTime) { 
   SetValueFromLexeme(lexeme, 0); 
  } else { 
            util.assert(this.allowsMultipleValues(), ""); 
   m_values.addElement(toObject(lexeme)); 
  } 
  m_firstTime = false; 
 } 
  
 protected void SetValueFromLexeme(String lexeme, int i) { 
  m_values.setSize(java.lang.Math.max(m_values.size(), 
i + 1)); 
  m_values.setElementAt(toObject(lexeme), i); 
 } 
  
 protected String getDefaultValue() { 
  return m_defaultValue.toString(); 
 } 
  
 protected void setDefaultValue(Object obj) { 
  m_defaultValue = obj; 
  m_values.setSize(java.lang.Math.max(m_values.size(),1)); 
  m_values.setElementAt(obj, 0); 
 } 
  
 protected static boolean isASwitch(String arg) { 
  return (arg.charAt(0) == '-'); 
 } 

 protected String                   m_name; 
 protected String                   m_message; 
 protected int                      m_flags; 
 protected String                   m_env_variable; 
 protected Vector                   m_values; 
 protected Object                   m_defaultValue; 
 protected boolean       m_firstTime; 
} 

Listing 6.  The StringToken class, an example of a sublcass of the Token class
 
// Copyright (c) 1998 Panos Kougiouris All Rights Reserved 
package psk.cmdline; 

/* 
 * The Implemetation of Tokens for Strings 
 * 
 * @version 1.0, 11/02/1998 
 * @author  Panos Kougiouris 
 */ 

public class StringToken extends Token 
{ 
 public StringToken( 
  String a_name, 
  String a_message, 
  String a_environment_variable, 
  int aTokenOptions, 
     String a_def_value) { 
  super(a_name, a_message, a_environment_variable, 
aTokenOptions); 
  setDefaultValue(a_def_value); 
 } 
  
 public String type() { 
  return "<String>"; 
 } 
  
 public String getValue() { 
  return getValue(0); 
 } 
  
 public String getValue(int i) { 
  return (String)m_values.elementAt(i); 
 } 
  
 public Object toObject(String lexeme) { 
  return lexeme; 
 } 
} 

Listing 7.  The ApplicationSettings class source code
 
// Copyright (c) 1998 Panos Kougiouris All Rights 
Reserved 
package psk.cmdline; 

/* 
 * The class that contains all the tokens and 
 * initiates the parsing 
 * 
 * @version  1.0, 11/04/1998 
 * @author Panos Kougiouris 
 */ 
  

public class ApplicationSettings 
{ 
 public ApplicationSettings() { 
  this(ApplicationSettingsOptions.optDoNotIgnoreUnknown); 
 } 
  
 public ApplicationSettings 
(int aApplicationSettingsOptions) { 
  m_argDescriptions = new java.util.Vector(); 
  m_flags = aApplicationSettingsOptions; 
 } 
  
 public void addToken(Token argum) { 
  for (int i = 0; i < m_argDescriptions.size(); i++) { 
   Token argDesc = ((Token)m_argDescriptions.elementAt(i)); 
  
   //Make sure we have no argument clash 
   if (argDesc.name().compareTo(argum.name()) == 0) { 
    System.err.print("ApplicationSettings ERROR: option \""); 
    System.err.print(argum.name()); 
    System.err.println("\"is used more than once"); 
    System.exit(-1); 
   } 
  
   // make sure there is only one that is non switch 
   if (!argDesc.isSwitch() && !argum.isSwitch()) { 
    System.err.print("ApplicationSettings ERROR: arguments 
defined in both '"); 
    System.err.print(argum.name()); 
    System.err.print("' and '"); 
    System.err.println(argDesc.name() + "'"); 
    System.err.println("Arguments should be defined 
only once."); 
    System.exit(-2); 
   } 
  } 
  m_argDescriptions.addElement(argum); 
 } 
  
 public void parseArgs(String[] args) throws Exception { 
  m_cmdLineArgs = new StringArrayIterator(args); 
  
  setEnvironmentValues(); 
  parseInternal(); 
 } 
  
 public void printUsage(String reason) throws Exception { 
  // Print the first line 
  System.err.print("Error: "); 
  System.err.println(reason + "\n"); 
  // Print the usage line 
  System.err.print("Usage: "); 
  System.err.print(m_programName + " "); 
  for (int i = 0; i < m_argDescriptions.size(); i++) { 
   Token arg = (Token)m_argDescriptions.elementAt(i); 
   arg.printUsage(System.err); 
  } 
  System.err.println("\n"); 
  
  // Print the explanations 
  for (int i = 0; i < m_argDescriptions.size(); i++) { 
   Token arg = (Token)m_argDescriptions.elementAt(i); 
   arg.printUsageExtended(System.err); 
  } 
  throw new Exception(reason); 
 } 
  
 //------------------------------------------------ 
  
 // This functions should be called only once 
 protected void parseNonSwitch() throws Exception { 
  for (int i = 0; i < m_argDescriptions.size(); i++) { 
   Token argDesc = 
((Token)m_argDescriptions.elementAt(i)); 
  
   if (!argDesc.parseArgument(m_cmdLineArgs)) continue; 
  
   // here should be the end... 
   if (!m_cmdLineArgs.EOF()) { 
    this.printUsage("too many commeand line arguments."); 
   } 
   return; 
  } 
  
  String str = "Unexepected argument "; 
  str += m_cmdLineArgs.get(); 
  if (!ignoreUnknownSwitches()) 
   this.printUsage(str); 
 } 
  
 protected void parseInternal() throws Exception { 
  // skip the name of the program 
  m_programName = "program"; 
  
  while (!m_cmdLineArgs.EOF()) { 
            try { 
       if (Token.isASwitch(m_cmdLineArgs.get())) { 
        this.parseSwitch(); 
       } else { 
        this.parseNonSwitch(); 
        break; 
       } 
            } catch (Exception ex) { 
                String str = ex.getMessage(); 
                if (ex.getClass() == 
NumberFormatException.class) { 
                    str = str + " "; 
                    str = str + " wrong argument type"; 
                } 
    // most likely 'argument expected' 
    this.printUsage(str); 
   } 
   m_cmdLineArgs.moveNext(); 
  } 
  
  for (int i = 0; i < m_argDescriptions.size(); i++) { 
   Token argDesc = 
((Token)m_argDescriptions.elementAt(i)); 
   if (!argDesc.isUsed() && argDesc.isRequired()) { 
    String str; 
    str = "missing required argument. Name: "; 
    str += argDesc.extendedName(); 
    this.printUsage(str); 
   } 
  } 
 } 
  
 protected void parseSwitch() throws Exception { 
  int i = 0; 
  for (i = 0; i < m_argDescriptions.size(); i++) { 
   Token argDesc = 
((Token)m_argDescriptions.elementAt(i)); 
   if (argDesc.ParseSwitch(m_cmdLineArgs)) return; 
  } 
  
  // We tried all the tokens and no one recognized 
  if (i >= m_argDescriptions.size()) { 
   String str= new String("Unknown option "); 
   str += m_cmdLineArgs.get(); 
   if (!ignoreUnknownSwitches()) 
    this.printUsage(str); 
  } 
 } 
  
 protected void setEnvironmentValues() { 
  for (int i = 0; i < m_argDescriptions.size(); i++) { 
   Token argDesc = 
((Token)m_argDescriptions.elementAt(i)); 
   if (argDesc.hasEnvironmentVariable()) { 
    String str = util.GetEnvVariable 
(argDesc.getEnvironmentVariable()); 
    if (str.length() != 0) { 
     argDesc.SetValueFromLexeme(str, 0); 
    } 
   } 
  } 
 } 
  
 protected boolean ignoreUnknownSwitches() { 
  return (m_flags & 
ApplicationSettingsOptions.optIgnoreUnknown) != 0; 
 } 
  
 // Data members 
 protected StringArrayIterator m_cmdLineArgs; 
 protected String              m_programName; 
 protected java.util.Vector    m_argDescriptions; 
// Vector of Argv objects 
 protected int                 m_flags; 
} 

Listing 8.  The util class (contains nonPure Java code.
 
If commented out the resulting code is Pure Java but the 
functionality is reduced) 
// Copyright (c) 1998 Panos Kougiouris All Rights Reserved 
package psk.cmdline; 

import com.ms.wfc.util.Debug.*; 
/* 
 * Java purists will want to comment out a few lines of code 
in this file 
 * 
 * 
 * @version  1.0,11/01/1998 
 * @author Panos Kougiouris 
 */ 

public class util 
{ 
    // If you do not tatrget the Microsoft Win32 VM 
 // comment out the following few lines. Always return 
    // "" from this function. It will just remove the 
    // env. variable functionality 
 // If you know how to get an env. variable in 
 // Pure Java pls e-mail me panos@acm.org 
    static public String GetEnvVariable(String name) { 
        StringBuffer strBuf = new StringBuffer(200); 
        int ret = com.ms.win32.Kernel32.GetEnvironmentVariable( 
               name, strBuf, 200); 
        if (ret != 0) { 
            return strBuf.toString(); 
        } else { 
            return ""; 
        } 
    } 
  
    // Just comment the line out for pure java environments 
    static public void assert(boolean cond, String msg) { 
        com.ms.wfc.util.Debug.assert(cond, msg); 
    } 
} 

  

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.