/*
* Copyright (C) Lennart Martens
*
* Contact: lennart.martens AT UGent.be (' AT ' to be replaced with '@')
*/
package com.compomics.util.general;
import org.apache.log4j.Logger;
import java.util.HashMap;
import java.util.ArrayList;
/*
* CVS information:
*
* $Revision: 1.3 $
* $Date: 2007/07/06 09:41:53 $
*/
/**
* This class provides a generic interface for the parsing of command-line
* arguments, options and flags. <br> Arguments are 'stand-alone' Strings,
* options are preceded by '--' without spaces (like in general GNU practice)
* and flags are indicated by '-' without spaces. <br> If there are options
* that in turn take arguments, these can be specified via a specific
* constructor.
*
* @author Lennart Martens
*/
public class CommandLineParser {
// Class specific log4j logger for CommandLineParser instances.
Logger logger = Logger.getLogger(CommandLineParser.class);
/**
* The command line all parsing will take place on.
*/
private String[] iCommandLine = null;
/**
* This boolean indicates whether any arguments were present at all.
*/
private boolean iHasArgs = false;
/**
* This String[] will hold all the flags.
*/
private String[] iFlags = null;
/**
* This String[] will hold all the options.
*/
private String[] iOptions = null;
/**
* This String[] will hold all the parameters.
*/
private String[] iParams = null;
/**
* This String[] will hold all the parameters for those options that take
* them. <br /> Structure is: key: option, value: param for that option. <br
* /> <i>Please note</i> that this HashMap is only initialized through the
* use of the <a href="#optionConstr">constructor</a> which takes a String[]
* with options that take arguments themselves!
*/
private HashMap iOptionParams = null;
/**
* The constructor requires the caller to provide it with a command line
* arguments String[] that will be the basis of the parsing.
*
* @param aCommandLine String[] with the command-line arguments.
*/
public CommandLineParser(String[] aCommandLine) {
this(aCommandLine, null);
}
/**
* <a name="optionConstr"></a> This constructor requests the command-line
* String[] as well as a String[] with a list of options which in turn take
* a parameter.
*
* @param aCommandLine String[] with the command-line arguments.
* @param aOptionArgs String[] with the options that take parameters
* themselves.
*/
public CommandLineParser(String[] aCommandLine, String[] aOptionArgs) {
if (aCommandLine != null) {
this.iCommandLine = (String[]) aCommandLine.clone();
}
// Add all the options that take a parameter to the
// HashMap.
iOptionParams = new HashMap();
// See if the option args [] is not 'null' or empty.
if (aOptionArgs != null && aOptionArgs.length > 0) {
for (int i = 0; i < aOptionArgs.length; i++) {
iOptionParams.put(aOptionArgs[i], null);
}
}
this.parseCommandLine();
}
/**
* This method returns 'true' if any arguments are present, 'false'
* otherwise. <br> You are well advised to call this method first, before
* attempting to retrieve any parameters, as 'null' might be returned.
*
* @return boolean 'true' if arguments are present, 'false' otherwise.
*/
public boolean hasArguments() {
return iHasArgs;
}
/**
* This method will report on all flags that have been found, or return an
* empty String[] if none were present.
*
* @return String[] with the flags or an empty array if none were found.
*/
public String[] getFlags() {
return iFlags;
}
/**
* This method will report on all options that have been found, or return an
* empty String[] if none were present.
*
* @return String[] with the options or an empty array if none were found.
*/
public String[] getOptions() {
return iOptions;
}
/**
* This method will report on all parameters that have been found, or return
* an empty String[] if none were present.
*
* @return String[] with the parameters or an empty array if none were
* found.
*/
public String[] getParameters() {
return iParams;
}
/**
* This method will report the option parameter for an option that can take
* a parameter itself. This option has to be specified in the <a
* href="#optionConstr">constructor</a> that allows you to pass a String[]
* with all the options that take a parameter themselves. <br>
* <b>Note!</b> If no parameter was present, or the option specified is not
* an option flagged as taking a parameter, 'null' is returned!
*
* @param aOption String with the option for which the parameter is to be
* retrieved.
* @return String with the parameter, if found, 'null' otherwise.
*/
public String getOptionParameter(String aOption) {
return (String) iOptionParams.get(aOption);
}
/**
* This method test whether the specified flag was set on the command line.
*
* @param aFlag String with the flag to check. Comparison is case-sensitive!
* @return boolean 'true' if the flag was set, 'false' otherwise.
*/
public boolean hasFlag(String aFlag) {
boolean present = false;
if (iFlags != null) {
for (int i = 0; i < iFlags.length; i++) {
String lFlag = iFlags[i];
if (lFlag.equals(aFlag)) {
present = true;
break;
}
}
}
return present;
}
/**
* This method will do the actual work of parsing the command-line arguments
* into an easily retrievable format.
*/
private void parseCommandLine() {
// First see if anything is there at all.
if ((iCommandLine == null) || (iCommandLine.length == 0)) {
iHasArgs = false;
} else {
// Flag that we have args.
iHasArgs = true;
// Arguments are present, let's see if we can parse them!
this.parseFlags();
this.parseOptions();
this.parseParameters();
}
}
/**
* This method searches for flags, and if present, adds them to the 'flags'
* String[]. <br /> Flags are demarcated by '-' and no separation space.
* More may be put behind a single '-'.
*/
private void parseFlags() {
// Tempstorage for the flags.
ArrayList tempFlags = new ArrayList();
// Get all Strings from the String[] that start with '-'.
// Note that we should make sure to avoid taking the Strings
// starting with '--' !
for (int i = 0; i < iCommandLine.length; i++) {
if (iCommandLine[i].startsWith("-") && !iCommandLine[i].startsWith("--")) {
String temp = iCommandLine[i];
// Get rid of the leading '-'.
temp = temp.substring(1);
// See if multiple flags are contained on this line.
if (temp.length() > 1) {
// Split it up in seperate letters, add each
// of these to the array.
for (int j = 0; j < temp.length(); j++) {
tempFlags.add(temp.substring(j, j + 1));
}
} else {
// Just add it.
tempFlags.add(temp);
}
}
}
// Now to transform our List into an array and set it.
iFlags = new String[tempFlags.size()];
tempFlags.toArray(iFlags);
}
/**
* This method searches for options, and if present, adds them to the
* 'options' String[]. <br /> Options are demarcated by '--' (akin to GNU
* notation) and no separation space. Some options may take an additional
* parameter themselves. If so, this has to be flagged by using the <a
* href="#optionConstr">constructor</a> that takes an additional String[]
* with the names of the options that take an additional argument.
*/
private void parseOptions() {
// We need something dynamic for a short while.
ArrayList tempOptions = new ArrayList();
ArrayList afterArray = new ArrayList();
for (int i = 0; i < iCommandLine.length; i++) {
String s = iCommandLine[i];
afterArray.add(s);
}
// Find all options.
// If one of these happens to take an argument,
// (i.e.: is a key in the iOptionParams Hashmap)
// retrieve that parameter (it is the next element
// in the args array) and add it as a value to
// that key in the HashMap.
for (int i = 0; i < iCommandLine.length; i++) {
if (iCommandLine[i].startsWith("--")) {
// Found one!
// Strip it of the '--'.
String temp = iCommandLine[i].substring(2);
// See if it takes a parameter.
if (iOptionParams.containsKey(temp)) {
// First of all, remove this option from the afterarray.
afterArray.remove(iCommandLine[i]);
// Okay, next element in the String has to be the one we're looking for.
StringBuffer param = new StringBuffer();
// Remove this next element too.
afterArray.remove(iCommandLine[i + 1]);
// See if it is a compound option (meaning it is encased in quotes).
if (iCommandLine[i + 1].startsWith("\"") && !(iCommandLine[i + 1].endsWith("\""))) {
param.append(iCommandLine[i + 1].substring(1));
int counter = 1;
do {
counter++;
param.append(" " + iCommandLine[i + counter]);
afterArray.remove(iCommandLine[i + counter]);
} while (!iCommandLine[i + counter].endsWith("\""));
param.deleteCharAt(param.length() - 1);
} else if (iCommandLine[i + 1].startsWith("\"") && iCommandLine[i + 1].endsWith("\"")) {
param.append(iCommandLine[i + 1].substring(1, iCommandLine[i + 1].length() - 1));
} else {
param.append(iCommandLine[i + 1]);
}
iOptionParams.put(temp, param.toString());
}
// Add it to the temp list.
tempOptions.add(temp);
}
}
// Convert the list into a String[] and store it.
iOptions = new String[tempOptions.size()];
tempOptions.toArray(iOptions);
// Overwrite the commandline array with the 'purified' one.
iCommandLine = new String[afterArray.size()];
afterArray.toArray(iCommandLine);
}
/**
* This method searches for parameters, and if present, adds them to the
* 'parameters' String[]. <br /> Parameters are identifiable through their
* lack of demarcation. <br /> Note that we should take care NOT to include
* parameters for options! There is a special check for that in the
* identifying loop!
*/
private void parseParameters() {
// Temp list storage.
ArrayList tempParams = new ArrayList();
// Find all params, ignore the params for options!
for (int i = 0; i < iCommandLine.length; i++) {
// Luckily, '--' also starts with '-' ;-).
if (!iCommandLine[i].startsWith("-")) {
// Okay, it is POTENTIALLY a parameter.
// First it has to survive the simple test against it
// being a parameter for an option.
// So we see if there is a previous argument,
// if there is, whether it starts with '--', and
// if it does, whether it is present in the
// iOptionParams HashMap as a key.
String temp = iCommandLine[i];
// If the current item has index of 0, there is no previous,
// therefor it cannot be an optionparam.
if ((i > 0) && (iCommandLine[i - 1].startsWith("--")) && (iOptionParams.containsKey(iCommandLine[i - 1].substring(2)))) {
// This is an option parameter.
// leave it alone.
} else {
tempParams.add(temp);
}
}
}
// Convert to array and be done with it.
iParams = new String[tempParams.size()];
tempParams.toArray(iParams);
}
}