/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.util;
import java.util.*;
/**
* Deals with commandline options (<code>String[] args</code>),
* for example to merge several sets of option specifications.
*
* An option consists of a key arg and 0...many value arguments.
* The method <code>parseArgs</code> will attempt to guess which arguments
* are keys and which are values, e.g. looking for a "-" prefix.
* This class can therefore be used for unknown sets of options.
* <p>
* To avoid fooling the algorithm with weird values (like "-273"), the expected
* option keys with their minimum number of values can be specified using the
* <code>registerOption</code> methods.
* This mechanism can later be extended to allow validation of required arguments.
* <p>
* TODO: logging and error handling
*
* @author hsommer
*/
public class CmdLineArgs
{
private Map m_registeredOptions;
// Map({CmdLineOption or String} key, String[] values)
private LinkedHashMap m_options;
public CmdLineArgs()
{
reset();
}
public void reset()
{
m_registeredOptions = new HashMap();
m_options = new LinkedHashMap();
}
///////// configuration ///////////////
public void registerOption(String name, int minValuesCount)
{
CmdLineRegisteredOption opt = new CmdLineRegisteredOption(name, minValuesCount);
registerOption(opt);
}
public void registerOption(CmdLineRegisteredOption opt)
{
if (opt == null) {
// todo
}
else {
m_registeredOptions.put(opt.getName(), opt);
if (opt.getAlternativeName() != null) {
m_registeredOptions.put(opt.getAlternativeName(), opt);
}
}
}
///////// execution ///////////////////
/**
* If called more than once without calling <code>reset()</code> in between,
* the options are merged in the style of a <code>Map</code>.
* @param args
*/
public void parseArgs(String[] args)
{
if (args == null) {
return;
}
int i = 0;
while (i < args.length)
{
if (!isKey(args, i))
{
System.out.println("stray option value " + args[i] + " found outside of any key.");
// cope with a hererogeneous map for the sake of not losing any arg
m_options.put(args[i], args[i]);
i++;
}
else
{
CmdLineOption opt = (CmdLineOption) m_registeredOptions.get(args[i]);
if (opt == null)
{
opt = new CmdLineOption(args[i]);
}
ArrayList values = new ArrayList();
int lastValIndex = i;
if (opt instanceof CmdLineRegisteredOption)
{
CmdLineRegisteredOption regOpt = (CmdLineRegisteredOption) opt;
lastValIndex = i + regOpt.getMinValueCount();
if (lastValIndex >= args.length)
{
System.out.println("warning: less than the required " + regOpt.getMinValueCount() +
" values given for option " + regOpt.getName());
}
}
// collect the values for the current option key
i++;
while (i < args.length && (i <= lastValIndex || !isKey(args, i)))
{
values.add(args[i]);
i++;
}
String[] valueArray = (String[]) values.toArray(new String[0]);
m_options.put(opt, valueArray);
}
}
}
/**
* Returns all arguments (both options and values).
* If arguments were parsed more than once, the values from a later call overwrite earlier ones.
* Options and values that could not be distinguished during parsing appear in the original order.
* @return
*/
public String[] getAllArgs()
{
ArrayList args = new ArrayList();
if (m_options != null)
{
for (Iterator iter = m_options.keySet().iterator(); iter.hasNext();)
{
Object element = iter.next();
if (element instanceof String)
{
args.add(element);
}
else
{
CmdLineOption clo = (CmdLineOption) element;
args.add(clo.getName());
String[] values = (String[]) m_options.get(clo);
args.addAll(Arrays.asList(values));
}
}
}
String[] argArray = (String[]) args.toArray(new String[0]);
return argArray;
}
/**
* Gets all arguments that were recognized by the parser.
* This includes arguments registered before parsing, as well as arguments recognized automatically.
* The values for these args can be obtained from {@link #getValues(CmdLineOption)}.
* @return
*/
public CmdLineOption[] getRecognizedArgs() {
ArrayList args = new ArrayList();
if (m_options != null) {
for (Iterator iter = m_options.keySet().iterator(); iter.hasNext();) {
Object element = iter.next();
if (element instanceof CmdLineOption) {
CmdLineOption clo = (CmdLineOption) element;
args.add(clo);
}
}
}
CmdLineOption[] argArray = (CmdLineOption[]) args.toArray(new CmdLineOption[0]);
return argArray;
}
/**
* Returns <code>true</code> if the given option <code>clo</code> appeared in the argument lists that were
* passed to <code>parseArgs</code>; <code>false</code> otherwise.
*
* @param clo
* @return boolean
*/
public boolean isSpecified(CmdLineRegisteredOption clo)
{
return (m_options.get(clo) != null );
}
public String[] getValues(CmdLineOption clo)
{
return ( (String[]) m_options.get(clo) );
}
/**
* Decides whether args[index] is a key or a value.
* Future implementations might need to look at args[other indices],
* that's why the full array is supplied.
* @param args
* @param index
* @return boolean
*/
private boolean isKey(String[] args, int index)
{
// first check the registered options
if (m_registeredOptions.containsKey(args[index]))
{
return true;
}
// then apply some heuristics that tells options (keys) from values
else if (args[index].startsWith("-"))
{
return true;
}
// did not pass for a key
else
{
return false;
}
}
}