blob: b9249401895df4512d73edbfd90af71c55005c99 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* A simple command line parser. This parser allows the caller to register a set of
* valid arguments. Each argument can have both a long and short format (e.g. -h and
* -hod). Note that -- (double dashes) is not required for long options as in
* standard GNU, though it is accepted.
*
* The caller then calls getNextOpt(). This returns a character, matching the short
* form of the argument. The caller can then obtain the value by calling one of the
* getVal functions.
*
* Author: Alan F. Gates (gates@)
* $Header:$
*/
package org.apache.pig.tools.cmdline;
import java.text.ParseException;
import java.util.HashMap;
import java.lang.AssertionError;
public class CmdLineParser
{
public enum ValueExpected {REQUIRED, OPTIONAL, NOT_ACCEPTED};
public static final char EndOfOpts = '-';
/**
* @param args String array of arguments passed to the program.
*/
public CmdLineParser(String[] args)
{
mArgs = args;
mArgNum = 0;
mShort = new HashMap<Character, ValueExpected>();
mLong = new HashMap<String, Character>();
}
/**
* Register a command line option.
* @param c Single character designator for this option. It cannot be '-'.
* @param s Full word designator for this option. This can be null, in which case
* no long designator will exist for this option.
* @param valueExpected If REQUIRED, a value will be expected with this option. If
* OPTIONAL a value will be accepted if it is seen.
* @throws AssertionError if there is no short option, or if this option has already been
* used.
*/
public void registerOpt(char c, String s, ValueExpected ve)
{
if (c == '-') {
throw new AssertionError("CmdLineParser: '-' is not a legal single character designator.");
}
Character cc = new Character(c);
if (mShort.put(cc, ve) != null) {
throw new AssertionError("CmdLineParser: You have already registered option " + cc.toString());
}
if (mLong != null) {
if (mLong.put(s, cc) != null) {
throw new AssertionError("CmdLineParser: You have already registered option " + s);
}
}
}
/**
* Get the next option.
* @return The short designator for the next argument. If there are no more arguments
* than the special designator CmdLineParser.EndOfOpts will be returned.
* @throws ParseException if an unknown option is found or an option that
* expects a value does not have one or a value that does not expect a value does have
* one.
*/
public char getNextOpt() throws ParseException
{
if (mArgNum >= mArgs.length) return EndOfOpts;
int offset = 1;
mVal = null;
try {
String arg = mArgs[mArgNum];
// If it doesn't start with a dash, we'll assume we've reached the end of the
// arguments. We need to decrement mArgNum because the finally at the end is
// going to increment it.
if (arg.charAt(0) != '-') {
mArgNum--;
return EndOfOpts;
}
// Strip any dashes off of the beginning
for (int i = 1; i < arg.length() && arg.charAt(i) == '-'; i++) offset++;
// If they passed us a - or -- then quit
if (offset == arg.length()) return EndOfOpts;
Character cc = null;
if (arg.substring(offset).length() == 1) {
cc = new Character(arg.substring(offset).charAt(0));
} else {
cc = mLong.get(arg.substring(offset));
if (cc == null) {
Integer ii = new Integer(mArgNum + 1);
StringBuilder errMsg = new StringBuilder();
errMsg.append("Found unknown option (");
errMsg.append(arg);
errMsg.append(") at position ");
errMsg.append(ii.toString());
throw new ParseException(errMsg.toString(), mArgNum);
}
}
ValueExpected ve = mShort.get(cc);
if (ve == null) {
Integer ii = new Integer(mArgNum + 1);
StringBuilder errMsg = new StringBuilder();
errMsg.append("Found unknown option (");
errMsg.append(arg);
errMsg.append(") at position ");
errMsg.append(ii.toString());
throw new ParseException(errMsg.toString(), mArgNum);
}
switch (ve) {
case NOT_ACCEPTED:
return cc.charValue();
case REQUIRED:
// If it requires an option, make sure there is one.
if (mArgNum + 1 >= mArgs.length || mArgs[mArgNum + 1].charAt(0) == '-') {
String errMsg = "Option " + arg +
" requires a value but you did not provide one.";
throw new ParseException(errMsg, mArgNum);
}
mVal = new String(mArgs[++mArgNum]);
return cc.charValue();
case OPTIONAL:
if (mArgNum + 1 < mArgs.length && mArgs[mArgNum + 1].charAt(0) != '-') {
mVal = new String(mArgs[++mArgNum]);
}
return cc.charValue();
default:
throw new AssertionError("Unknown valueExpected state");
}
} finally {
mArgNum++;
}
}
/**
* Get any remaining arguments.
* @return In general this function will null.
* Only if the caller passed a '-' or '--' followed by other arguments. In that case
* the remainder of the args array will be returned.
*/
public String[] getRemainingArgs()
{
if (mArgNum == mArgs.length) return null;
String[] remainders = new String[mArgs.length - mArgNum];
System.arraycopy(mArgs, mArgNum, remainders, 0, remainders.length);
return remainders;
}
/**
* Get the value, as a string.
* @return The value associated with the current option. If there is no value,
* then null will be returned.
*/
public String getValStr()
{
return mVal;
}
/**
* Get the value, as an Integer.
* @return The value associated with the current option. If there is not value, then
* null will be returned.
* @throws NumberFormatException if the value cannot be converted to an integer.
*/
public Integer getValInt() throws NumberFormatException
{
if (mVal == null) return null;
else return new Integer(mVal);
}
private String[] mArgs;
private HashMap<Character, ValueExpected> mShort;
private HashMap<String, Character> mLong;
private int mArgNum;
private String mVal;
}