package org.jboss.axis.utils;
import java.text.ParseException;
import java.util.Hashtable;
import java.util.Vector;
public final class CLArgsParser
{
private static final int STATE_NORMAL = 0;
private static final int STATE_REQUIRE_2ARGS = 1;
private static final int STATE_REQUIRE_ARG = 2;
private static final int STATE_OPTIONAL_ARG = 3;
private static final int STATE_NO_OPTIONS = 4;
private static final int STATE_OPTION_MODE = 5;
private static final int TOKEN_SEPARATOR = 0;
private static final int TOKEN_STRING = 1;
private static final char[] ARG2_SEPARATORS =
new char[]{(char)0, '=', '-'};
private static final char[] ARG_SEPARATORS =
new char[]{(char)0, '='};
private static final char[] NULL_SEPARATORS =
new char[]{(char)0};
private final CLOptionDescriptor[] m_optionDescriptors;
private final Vector m_options;
private Hashtable m_optionIndex;
private final ParserControl m_control;
private String m_errorMessage;
private String[] m_unparsedArgs = new String[]{};
private char ch;
private String[] args;
private boolean isLong;
private int argIndex;
private int stringIndex;
private int stringLength;
private static final int INVALID = Integer.MAX_VALUE;
private int m_lastChar = INVALID;
private int m_lastOptionId;
private CLOption m_option;
private int m_state = STATE_NORMAL;
public final String[] getUnparsedArgs()
{
return m_unparsedArgs;
}
public final Vector getArguments()
{
return m_options;
}
public final CLOption getArgumentById(final int id)
{
return (CLOption)m_optionIndex.get(new Integer(id));
}
public final CLOption getArgumentByName(final String name)
{
return (CLOption)m_optionIndex.get(name);
}
private final CLOptionDescriptor getDescriptorFor(final int id)
{
for (int i = 0; i < m_optionDescriptors.length; i++)
{
if (m_optionDescriptors[i].getId() == id)
{
return m_optionDescriptors[i];
}
}
return null;
}
private final CLOptionDescriptor getDescriptorFor(final String name)
{
for (int i = 0; i < m_optionDescriptors.length; i++)
{
if (m_optionDescriptors[i].getName().equals(name))
{
return m_optionDescriptors[i];
}
}
return null;
}
public final String getErrorString()
{
return m_errorMessage;
}
private final int getStateFor(final CLOptionDescriptor descriptor)
{
int flags = descriptor.getFlags();
if ((flags & CLOptionDescriptor.ARGUMENTS_REQUIRED_2) ==
CLOptionDescriptor.ARGUMENTS_REQUIRED_2)
{
return STATE_REQUIRE_2ARGS;
}
else if ((flags & CLOptionDescriptor.ARGUMENT_REQUIRED) ==
CLOptionDescriptor.ARGUMENT_REQUIRED)
{
return STATE_REQUIRE_ARG;
}
else if ((flags & CLOptionDescriptor.ARGUMENT_OPTIONAL) ==
CLOptionDescriptor.ARGUMENT_OPTIONAL)
{
return STATE_OPTIONAL_ARG;
}
else
{
return STATE_NORMAL;
}
}
public CLArgsParser(final String[] args,
final CLOptionDescriptor[] optionDescriptors,
final ParserControl control)
{
m_optionDescriptors = optionDescriptors;
m_control = control;
m_options = new Vector();
this.args = args;
try
{
parse();
checkIncompatibilities(m_options);
buildOptionIndex();
}
catch (final ParseException pe)
{
m_errorMessage = pe.getMessage();
}
}
private final void checkIncompatibilities(final Vector arguments)
throws ParseException
{
final int size = arguments.size();
for (int i = 0; i < size; i++)
{
final CLOption option = (CLOption)arguments.elementAt(i);
final int id = option.getId();
final CLOptionDescriptor descriptor = getDescriptorFor(id);
if (null == descriptor)
{
continue;
}
final int[] incompatible = descriptor.getIncompatible();
checkIncompatible(arguments, incompatible, i);
}
}
private final void checkIncompatible(final Vector arguments,
final int[] incompatible,
final int original)
throws ParseException
{
final int size = arguments.size();
for (int i = 0; i < size; i++)
{
if (original == i)
{
continue;
}
final CLOption option = (CLOption)arguments.elementAt(i);
final int id = option.getId();
for (int j = 0; j < incompatible.length; j++)
{
if (id == incompatible[j])
{
final CLOption originalOption = (CLOption)arguments.elementAt(original);
final int originalId = originalOption.getId();
String message = null;
if (id == originalId)
{
message =
"Duplicate options for " + describeDualOption(originalId) +
" found.";
}
else
{
message = "Incompatible options -" +
describeDualOption(id) + " and " +
describeDualOption(originalId) + " found.";
}
throw new ParseException(message, 0);
}
}
}
}
private final String describeDualOption(final int id)
{
final CLOptionDescriptor descriptor = getDescriptorFor(id);
if (null == descriptor)
{
return "<parameter>";
}
else
{
final StringBuffer sb = new StringBuffer();
boolean hasCharOption = false;
if (Character.isLetter((char)id))
{
sb.append('-');
sb.append((char)id);
hasCharOption = true;
}
final String longOption = descriptor.getName();
if (null != longOption)
{
if (hasCharOption)
{
sb.append('/');
}
sb.append("--");
sb.append(longOption);
}
return sb.toString();
}
}
public CLArgsParser(final String[] args,
final CLOptionDescriptor[] optionDescriptors)
{
this(args, optionDescriptors, null);
}
private final String[] subArray(final String[] array,
final int index,
final int charIndex)
{
final int remaining = array.length - index;
final String[] result = new String[remaining];
if (remaining > 1)
{
System.arraycopy(array, index + 1, result, 1, remaining - 1);
}
result[0] = array[index].substring(charIndex - 1);
return result;
}
private final void parse()
throws ParseException
{
if (0 == args.length)
{
return;
}
stringLength = args[argIndex].length();
while (true)
{
ch = peekAtChar();
if (argIndex >= args.length)
{
break;
}
if (null != m_control && m_control.isFinished(m_lastOptionId))
{
m_unparsedArgs = subArray(args, argIndex, stringIndex);
return;
}
if (STATE_OPTION_MODE == m_state)
{
if (0 == ch)
{
getChar(); m_state = STATE_NORMAL;
}
else
{
parseShortOption();
}
}
else if (STATE_NORMAL == m_state)
{
parseNormal();
}
else if (STATE_NO_OPTIONS == m_state)
{
addOption(new CLOption(args[argIndex++]));
}
else if (STATE_OPTIONAL_ARG == m_state && '-' == ch)
{
m_state = STATE_NORMAL;
addOption(m_option);
}
else
{
parseArguments();
}
}
if (m_option != null)
{
if (STATE_OPTIONAL_ARG == m_state)
{
m_options.addElement(m_option);
}
else if (STATE_REQUIRE_ARG == m_state)
{
final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getId());
final String message =
"Missing argument to option " + getOptionDescription(descriptor);
throw new ParseException(message, 0);
}
else if (STATE_REQUIRE_2ARGS == m_state)
{
if (1 == m_option.getArgumentCount())
{
m_option.addArgument("");
m_options.addElement(m_option);
}
else
{
final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getId());
final String message =
"Missing argument to option " + getOptionDescription(descriptor);
throw new ParseException(message, 0);
}
}
else
{
throw new ParseException("IllegalState " + m_state + ": " + m_option, 0);
}
}
}
private final String getOptionDescription(final CLOptionDescriptor descriptor)
{
if (isLong)
{
return "--" + descriptor.getName();
}
else
{
return "-" + (char)descriptor.getId();
}
}
private final char peekAtChar()
{
if (INVALID == m_lastChar)
{
m_lastChar = readChar();
}
return (char)m_lastChar;
}
private final char getChar()
{
if (INVALID != m_lastChar)
{
final char result = (char)m_lastChar;
m_lastChar = INVALID;
return result;
}
else
{
return readChar();
}
}
private final char readChar()
{
if (stringIndex >= stringLength)
{
argIndex++;
stringIndex = 0;
if (argIndex < args.length)
{
stringLength = args[argIndex].length();
}
else
{
stringLength = 0;
}
return 0;
}
if (argIndex >= args.length)
return 0;
return args[argIndex].charAt(stringIndex++);
}
private final Token nextToken(final char[] separators)
{
ch = getChar();
if (isSeparator(ch, separators))
{
ch = getChar();
return new Token(TOKEN_SEPARATOR, null);
}
final StringBuffer sb = new StringBuffer();
do
{
sb.append(ch);
ch = getChar();
}
while (!isSeparator(ch, separators));
return new Token(TOKEN_STRING, sb.toString());
}
private final boolean isSeparator(final char ch, final char[] separators)
{
for (int i = 0; i < separators.length; i++)
{
if (ch == separators[i])
{
return true;
}
}
return false;
}
private final void addOption(final CLOption option)
{
m_options.addElement(option);
m_lastOptionId = option.getId();
m_option = null;
}
private final void parseOption(final CLOptionDescriptor descriptor,
final String optionString)
throws ParseException
{
if (null == descriptor)
{
throw new ParseException("Unknown option " + optionString, 0);
}
m_state = getStateFor(descriptor);
m_option = new CLOption(descriptor.getId());
if (STATE_NORMAL == m_state)
{
addOption(m_option);
}
}
private final void parseShortOption()
throws ParseException
{
ch = getChar();
final CLOptionDescriptor descriptor = getDescriptorFor(ch);
isLong = false;
parseOption(descriptor, "-" + ch);
if (STATE_NORMAL == m_state)
{
m_state = STATE_OPTION_MODE;
}
}
private final void parseArguments()
throws ParseException
{
if (STATE_REQUIRE_ARG == m_state)
{
if ('=' == ch || 0 == ch)
{
getChar();
}
final Token token = nextToken(NULL_SEPARATORS);
m_option.addArgument(token.getValue());
addOption(m_option);
m_state = STATE_NORMAL;
}
else if (STATE_OPTIONAL_ARG == m_state)
{
if ('-' == ch || 0 == ch)
{
getChar(); addOption(m_option);
m_state = STATE_NORMAL;
return;
}
if ('=' == ch)
{
getChar();
}
final Token token = nextToken(NULL_SEPARATORS);
m_option.addArgument(token.getValue());
addOption(m_option);
m_state = STATE_NORMAL;
}
else if (STATE_REQUIRE_2ARGS == m_state)
{
if (0 == m_option.getArgumentCount())
{
final Token token = nextToken(ARG_SEPARATORS);
if (TOKEN_SEPARATOR == token.getType())
{
final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getId());
final String message =
"Unable to parse first argument for option " +
getOptionDescription(descriptor);
throw new ParseException(message, 0);
}
else
{
m_option.addArgument(token.getValue());
}
}
else {
final StringBuffer sb = new StringBuffer();
ch = getChar();
if ('-' == ch)
{
m_lastChar = ch;
}
while (!isSeparator(ch, ARG2_SEPARATORS))
{
sb.append(ch);
ch = getChar();
}
final String argument = sb.toString();
m_option.addArgument(argument);
addOption(m_option);
m_option = null;
m_state = STATE_NORMAL;
}
}
}
private final void parseNormal()
throws ParseException
{
if ('-' != ch)
{
final String argument = nextToken(NULL_SEPARATORS).getValue();
addOption(new CLOption(argument));
m_state = STATE_NORMAL;
}
else
{
getChar();
if (0 == peekAtChar())
{
throw new ParseException("Malformed option -", 0);
}
else
{
ch = peekAtChar();
if ('-' != ch)
{
parseShortOption();
}
else
{
getChar();
if (0 == peekAtChar())
{
getChar();
m_state = STATE_NO_OPTIONS;
}
else
{
final String optionName = nextToken(ARG_SEPARATORS).getValue();
final CLOptionDescriptor descriptor = getDescriptorFor(optionName);
isLong = true;
parseOption(descriptor, "--" + optionName);
}
}
}
}
}
private final void buildOptionIndex()
{
m_optionIndex = new Hashtable(m_options.size() * 2);
for (int i = 0; i < m_options.size(); i++)
{
final CLOption option = (CLOption)m_options.get(i);
final CLOptionDescriptor optionDescriptor =
getDescriptorFor(option.getId());
m_optionIndex.put(new Integer(option.getId()), option);
if (null != optionDescriptor)
{
m_optionIndex.put(optionDescriptor.getName(), option);
}
}
}
}