OptsParserpublic final class OptsParser extends Object implements IOptsParser
Fields Summary |
---|
private final String | m_msgPrefix | private final OptDefMetadata | m_metadata | private static final int | CANONICAL_OPT_PREFIX | private static final String[] | OPT_PREFIXES | private static final char[] | OPT_VALUE_SEPARATORS | private static final int | STATE_OPT | private static final int | STATE_OPT_VALUE | private static final int | STATE_FREE_ARGS | private static final int | STATE_ERROR |
Constructors Summary |
---|
OptsParser(String metadataResourceName, ClassLoader loader, String[] usageOpts)
KEYWORDS = new HashMap (17);
KEYWORDS.put (Token.OPTIONAL.getValue (), Token.OPTIONAL);
KEYWORDS.put (Token.REQUIRED.getValue (), Token.REQUIRED);
KEYWORDS.put (Token.VALUES.getValue (), Token.VALUES);
KEYWORDS.put (Token.REQUIRES.getValue (), Token.REQUIRES);
KEYWORDS.put (Token.EXCLUDES.getValue (), Token.EXCLUDES);
KEYWORDS.put (Token.MERGEABLE.getValue (), Token.MERGEABLE);
KEYWORDS.put (Token.DETAILEDONLY.getValue (), Token.DETAILEDONLY);
KEYWORDS.put (Token.PATTERN.getValue (), Token.PATTERN);
this (metadataResourceName, loader, null, usageOpts);
| OptsParser(String metadataResourceName, ClassLoader loader, String msgPrefix, String[] usageOpts)
if (metadataResourceName == null) throw new IllegalArgumentException ("null input: metadataResourceName");
m_msgPrefix = msgPrefix;
InputStream in = null;
try
{
in = ResourceLoader.getResourceAsStream (metadataResourceName, loader);
if (in == null)
throw new IllegalArgumentException ("resource [" + metadataResourceName + "] could not be loaded via [" + loader + "]");
// TODO: encoding
final Reader rin = new InputStreamReader (in);
m_metadata = parseOptDefMetadata (rin, usageOpts);
}
finally
{
if (in != null) try { in.close (); } catch (IOException ignore) {}
}
|
Methods Summary |
---|
private java.lang.String | formatMessage(java.lang.String msg)
if (m_msgPrefix == null) return msg;
else
{
return m_msgPrefix.concat (msg);
}
| private static java.lang.String | getOptCanonicalName(java.lang.String n, com.vladium.util.args.OptsParser$OptDef optdef)
if (optdef.isPattern ())
{
final String canonicalPattern = optdef.getCanonicalName ();
final String [] patterns = optdef.getNames ();
for (int p = 0; p < patterns.length; ++ p)
{
final String pattern = patterns [p];
if (n.startsWith (pattern))
{
return canonicalPattern.concat (n.substring (pattern.length ()));
}
}
// this should never happen:
throw new IllegalStateException ("failed to detect pattern prefix for [" + n + "]");
}
else
{
return optdef.getCanonicalName ();
}
| private static void | getOptNameAndValue(java.lang.String av, java.lang.String[] nv)
nv [0] = null;
nv [1] = null;
for (int p = 0; p < OPT_PREFIXES.length; ++ p)
{
if ((av.startsWith (OPT_PREFIXES [p])) && (av.length () > OPT_PREFIXES [p].length ()))
{
final String name = av.substring (OPT_PREFIXES [p].length ()); // with a possible value after a separator
char separator = 0;
int sindex = Integer.MAX_VALUE;
for (int s = 0; s < OPT_VALUE_SEPARATORS.length; ++ s)
{
final int index = name.indexOf (OPT_VALUE_SEPARATORS [s]);
if ((index > 0) && (index < sindex))
{
separator = OPT_VALUE_SEPARATORS [s];
sindex = index;
}
}
if (separator != 0)
{
nv [0] = name.substring (0, sindex);
nv [1] = name.substring (sindex + 1);
}
else
{
nv [0] = name;
}
return;
}
}
| private static boolean | isOpt(java.lang.String av, int valueCount, com.vladium.util.args.OptsParser$OptDef optdef)
if (optdef != null)
{
// if the current optdef calls for more values, consume the next token
// as an op value greedily, without looking at its prefix:
final int [] cardinality = optdef.getValueCardinality ();
if (valueCount < cardinality [1]) return false;
}
// else check av's prefix:
for (int p = 0; p < OPT_PREFIXES.length; ++ p)
{
if (av.startsWith (OPT_PREFIXES [p]))
return (av.length () > OPT_PREFIXES [p].length ());
}
return false;
| public synchronized IOpts | parse(java.lang.String[] args)
if (args == null) throw new IllegalArgumentException ("null input: args");
final Opts opts = new Opts ();
{
final String [] nv = new String [2]; // out buffer for getOptNameAndValue()
final String [] pp = new String [1]; // out buffer for getOptDef()
// running state/current vars:
int state = STATE_OPT;
OptDef optdef = null;
Opt opt = null;
String value = null;
int valueCount = 0;
int a;
scan: for (a = 0; a < args.length; )
{
final String av = args [a];
if (av == null) throw new IllegalArgumentException ("null input: args[" + a + "]");
//System.out.println ("[state: " + state + "] av = " + av);
switch (state)
{
case STATE_OPT:
{
if (isOpt (av, valueCount, optdef))
{
// 'av' looks like an option: get its name and see if it
// is in the metadata
valueCount = 0;
getOptNameAndValue (av, nv); // this can leave nv[1] as null
// [assertion: nv [0] != null]
final String optName = nv [0]; // is not necessarily canonical
optdef = m_metadata.getOptDef (optName, pp); // pp [0] is always set by this
if (optdef == null)
{
// unknown option:
// TODO: coded messages?
opts.addError (formatMessage ("unknown option \'" + optName + "\'"));
state = STATE_ERROR;
}
else
{
// merge if necessary:
final String canonicalName = getOptCanonicalName (optName, optdef);
final String patternPrefix = pp [0];
opt = opts.getOpt (canonicalName);
if (optdef.isMergeable ())
{
if (opt == null)
{
opt = new Opt (optName, canonicalName, patternPrefix);
opts.addOpt (opt, optdef, optName);
}
}
else
{
if (opt == null)
{
opt = new Opt (optName, canonicalName, patternPrefix);
opts.addOpt (opt, optdef, optName);
}
else
{
opts.addError (formatMessage ("option \'" + optName + "\' cannot be specified more than once"));
state = STATE_ERROR;
}
}
value = nv [1];
if (value == null) ++ a;
state = STATE_OPT_VALUE;
}
}
else
{
// not in STATE_OPT_VALUE and 'av' does not look
// like an option: the rest of args are free
state = STATE_FREE_ARGS;
}
}
break;
case STATE_OPT_VALUE:
{
// [assertion: opt != null and optdef != null]
if (value != null)
{
// value specified explicitly using the <name>separator<value> syntax:
// [don't shift a]
valueCount = 1;
final int [] cardinality = optdef.getValueCardinality ();
if (cardinality [1] < 1)
{
opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept values: \'" + value + "\'"));
state = STATE_ERROR;
}
else
{
++ a;
opt.addValue (value);
}
}
else
{
value = args [a];
final int [] cardinality = optdef.getValueCardinality ();
if (isOpt (value, valueCount, optdef))
{
if (valueCount < cardinality [0])
{
opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept fewer than " + cardinality [0] + " value(s)"));
state = STATE_ERROR;
}
else
state = STATE_OPT;
}
else
{
if (valueCount < cardinality [1])
{
++ valueCount;
++ a;
opt.addValue (value);
}
else
{
// this check is redundant:
// if (valueCount < cardinality [0])
// {
// opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept fewer than " + cardinality [0] + " value(s)"));
//
// state = STATE_ERROR;
// }
// else
state = STATE_FREE_ARGS;
}
}
}
value = null;
}
break;
case STATE_FREE_ARGS:
{
if (isOpt (args [a], valueCount, optdef))
{
state = STATE_OPT;
}
else
{
opts.setFreeArgs (args, a);
break scan;
}
}
break;
case STATE_ERROR:
{
break scan; // TODO: could use the current value of 'a' for a better error message
}
} // end of switch
}
if (a == args.length)
{
if (opt != null) // validate the last option's min cardinality
{
final int [] cardinality = optdef.getValueCardinality ();
if (valueCount < cardinality [0])
{
opts.addError (formatMessage ("option \'" + opt.getName () + "\' does not accept fewer than " + cardinality [0] + " value(s)"));
}
}
else
{
opts.setFreeArgs (args, a);
}
}
} // end of 'args' parsing
final IOpt [] specified = opts.getOpts ();
if (specified != null)
{
// validation: all required parameters must be specified
final Set /* String(canonical name) */ required = new HashSet ();
required.addAll (m_metadata.getRequiredOpts ());
for (int s = 0; s < specified.length; ++ s)
{
required.remove (specified [s].getCanonicalName ());
}
if (! required.isEmpty ())
{
for (Iterator i = required.iterator (); i.hasNext (); )
{
opts.addError (formatMessage ("missing required option \'" + (String) i.next () + "\'"));
}
}
for (int s = 0; s < specified.length; ++ s)
{
final IOpt opt = specified [s];
final OptDef optdef = m_metadata.getOptDef (opt.getCanonicalName (), null);
// // validation: value cardinality constraints
//
// final int [] cardinality = optdef.getValueCardinality ();
// if (opt.getValueCount () < cardinality [0])
// opts.addError (formatMessage ("option \'" + opt.getName () + "\' must have at least " + cardinality [0] + " value(s)"));
// else if (opt.getValueCount () > cardinality [1])
// opts.addError (formatMessage ("option \'" + opt.getName () + "\' must not have more than " + cardinality [1] + " value(s)"));
// validation: "requires" constraints
final String [] requires = optdef.getRequiresSet (); // not canonicalized
if (requires != null)
{
for (int r = 0; r < requires.length; ++ r)
{
if (opts.getOpt (requires [r]) == null)
opts.addError (formatMessage ("option \'" + opt.getName () + "\' requires option \'" + requires [r] + "\'"));
}
}
// validation: "not with" constraints
final String [] excludes = optdef.getExcludesSet (); // not canonicalized
if (excludes != null)
{
for (int x = 0; x < excludes.length; ++ x)
{
final Opt xopt = opts.getOpt (excludes [x]);
if (xopt != null)
opts.addError (formatMessage ("option \'" + opt.getName () + "\' cannot be used with option \'" + xopt.getName () + "\'"));
}
}
// side effect: determine if usage is requested
if (optdef.isUsage ())
{
opts.setUsageRequested (opt.getName ().equals (opt.getCanonicalName ()) ? SHORT_USAGE : DETAILED_USAGE);
}
}
}
return opts;
| private static com.vladium.util.args.OptsParser$OptDefMetadata | parseOptDefMetadata(java.io.Reader in, java.lang.String[] usageOpts)
// end of nested class
final MetadataParser parser = new MetadataParser ();
final OptDef [] optdefs = parser.parse (in);
// validate:
// for (int o = 0; o < optdefs.length; ++ o)
// {
// final OptDef optdef = optdefs [o];
// final int [] cardinality = optdef.getValueCardinality ();
//
// if (optdef.isMergeable ())
// {
// if ((cardinality [1] != 0) && (cardinality [1] != Integer.MAX_VALUE))
// throw new IllegalArgumentException ("option [" + optdef.getCanonicalName () + "] is mergeable and can only specify {0, +inf} for max value cardinality: " + cardinality [1]);
// }
// }
final OptDefMetadata result = new OptDefMetadata ();
for (int o = 0; o < optdefs.length; ++ o)
{
result.addOptDef (optdefs [o]);
}
// add usage opts:
if (usageOpts != null)
{
final OptDef usage = new OptDef (true);
usage.setNames (usageOpts);
usage.setDescription ("display usage information");
usage.setValueCardinality (OptDef.C_ZERO);
usage.setRequired (false);
usage.setDetailedOnly (false);
usage.setMergeable (false);
result.addOptDef (usage);
}
// TODO: fix this to be pattern-savvy
for (int o = 0; o < optdefs.length; ++ o)
{
final OptDef optdef = optdefs [o];
final String [] requires = optdef.getRequiresSet ();
if (requires != null)
{
for (int r = 0; r < requires.length; ++ r)
{
final OptDef ropt = result.getOptDef (requires [r], null);
if (ropt == null)
throw new IllegalArgumentException ("option [" + optdef.getCanonicalName () + "] specifies an unknown option [" + requires [r] + "] in its \'requires\' set");
if (ropt == optdef)
throw new IllegalArgumentException ("option [" + optdef.getCanonicalName () + "] specifies itself in its \'requires\' set");
}
}
final String [] excludes = optdef.getExcludesSet ();
if (excludes != null)
{
for (int x = 0; x < excludes.length; ++ x)
{
final OptDef xopt = result.getOptDef (excludes [x], null);
if (xopt == null)
throw new IllegalArgumentException ("option [" + optdef.getCanonicalName () + "] specifies an unknown option [" + excludes [x] + "] in its \'excludes\' set");
if (xopt.isRequired ())
throw new IllegalArgumentException ("option [" + optdef.getCanonicalName () + "] specifies a required option [" + excludes [x] + "] in its \'excludes\' set");
if (xopt == optdef)
throw new IllegalArgumentException ("option [" + optdef.getCanonicalName () + "] specifies itself in its \'excludes\' set");
}
}
}
return result;
| public synchronized void | usage(java.io.PrintWriter out, int level, int width)
// TODO: use width
// TODO: cache?
final String prefix = OPT_PREFIXES [CANONICAL_OPT_PREFIX];
for (Iterator i = m_metadata.getOptDefs (); i.hasNext (); )
{
final OptDef optdef = (OptDef) i.next ();
if ((level < 2) && optdef.isDetailedOnly ()) // skip detailed usage only options
continue;
final StringBuffer line = new StringBuffer (" ");
final String canonicalName = optdef.getCanonicalName ();
final boolean isPattern = optdef.isPattern ();
line.append (prefix);
line.append (canonicalName);
if (isPattern) line.append ('*");
final String [] names = optdef.getNames ();
for (int n = 0; n < names.length; ++ n)
{
final String name = names [n];
if (! name.equals (canonicalName))
{
line.append (", ");
line.append (prefix);
line.append (name);
if (isPattern) line.append ('*");
}
}
final String vmnemonic = optdef.getValueMnemonic ();
if (vmnemonic != null)
{
line.append (' ");
line.append (vmnemonic);
}
int padding = 16 - line.length ();
if (padding < 2)
{
// end the current line
out.println (line);
line.setLength (0);
for (int p = 0; p < 16; ++ p) line.append (' ");
}
else
{
for (int p = 0; p < padding; ++ p) line.append (' ");
}
if (optdef.isRequired ()) line.append ("{required} ");
line.append (optdef.getDescription ());
out.println (line);
}
if (level < DETAILED_USAGE)
{
final OptDef usageOptDef = m_metadata.getUsageOptDef ();
if ((usageOptDef != null) && (usageOptDef.getNames () != null) && (usageOptDef.getNames ().length > 1))
{
out.println ();
out.println (" {use '" + usageOptDef.getNames () [1] + "' option to see detailed usage help}");
}
}
|
|