This repository has been archived on 2024-04-07. You can view files and clone it, but cannot push or open issues or pull requests.
chovy-gm/NDesk.Options/OptionSet.cs

700 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace NDesk.Options
{
public class OptionSet : KeyedCollection<string, Option>
{
private sealed class ActionOption : Option
{
private Action<OptionValueCollection> action;
public ActionOption(string prototype, string description, int count, Action<OptionValueCollection> action)
: base(prototype, description, count)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
this.action = action;
}
protected override void OnParseComplete(OptionContext c)
{
action(c.OptionValues);
}
}
private sealed class ActionOption<T> : Option
{
private Action<T> action;
public ActionOption(string prototype, string description, Action<T> action)
: base(prototype, description, 1)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
this.action = action;
}
protected override void OnParseComplete(OptionContext c)
{
action(Option.Parse<T>(c.OptionValues[0], c));
}
}
private sealed class ActionOption<TKey, TValue> : Option
{
private OptionAction<TKey, TValue> action;
public ActionOption(string prototype, string description, OptionAction<TKey, TValue> action)
: base(prototype, description, 2)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
this.action = action;
}
protected override void OnParseComplete(OptionContext c)
{
action(Option.Parse<TKey>(c.OptionValues[0], c), Option.Parse<TValue>(c.OptionValues[1], c));
}
}
private const int OptionWidth = 29;
private Converter<string, string> localizer;
private readonly Regex ValueOption = new Regex("^(?<flag>--|-|/)(?<name>[^:=]+)((?<sep>[:=])(?<value>.*))?$");
public Converter<string, string> MessageLocalizer
{
get
{
return localizer;
}
}
public OptionSet()
: this((string f) => f)
{
}
public OptionSet(Converter<string, string> localizer)
{
this.localizer = localizer;
}
protected override string GetKeyForItem(Option item)
{
if (item == null)
{
throw new ArgumentNullException("option");
}
if (item.Names != null && item.Names.Length > 0)
{
return item.Names[0];
}
throw new InvalidOperationException("Option has no names!");
}
[Obsolete("Use KeyedCollection.this[string]")]
protected Option GetOptionForName(string option)
{
if (option == null)
{
throw new ArgumentNullException("option");
}
try
{
return base[option];
}
catch (KeyNotFoundException)
{
return null;
}
}
protected override void InsertItem(int index, Option item)
{
base.InsertItem(index, item);
AddImpl(item);
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
Option option = base.Items[index];
for (int i = 1; i < option.Names.Length; i++)
{
base.Dictionary.Remove(option.Names[i]);
}
}
protected override void SetItem(int index, Option item)
{
base.SetItem(index, item);
RemoveItem(index);
AddImpl(item);
}
private void AddImpl(Option option)
{
if (option == null)
{
throw new ArgumentNullException("option");
}
List<string> list = new List<string>(option.Names.Length);
try
{
for (int i = 1; i < option.Names.Length; i++)
{
base.Dictionary.Add(option.Names[i], option);
list.Add(option.Names[i]);
}
}
catch (Exception)
{
foreach (string item in list)
{
base.Dictionary.Remove(item);
}
throw;
}
}
public new OptionSet Add(Option option)
{
base.Add(option);
return this;
}
public OptionSet Add(string prototype, Action<string> action)
{
return Add(prototype, null, action);
}
public OptionSet Add(string prototype, string description, Action<string> action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
Option item = new ActionOption(prototype, description, 1, delegate(OptionValueCollection v)
{
action(v[0]);
});
base.Add(item);
return this;
}
public OptionSet Add(string prototype, OptionAction<string, string> action)
{
return Add(prototype, null, action);
}
public OptionSet Add(string prototype, string description, OptionAction<string, string> action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
Option item = new ActionOption(prototype, description, 2, delegate(OptionValueCollection v)
{
action(v[0], v[1]);
});
base.Add(item);
return this;
}
public OptionSet Add<T>(string prototype, Action<T> action)
{
return Add(prototype, null, action);
}
public OptionSet Add<T>(string prototype, string description, Action<T> action)
{
return Add(new ActionOption<T>(prototype, description, action));
}
public OptionSet Add<TKey, TValue>(string prototype, OptionAction<TKey, TValue> action)
{
return Add(prototype, null, action);
}
public OptionSet Add<TKey, TValue>(string prototype, string description, OptionAction<TKey, TValue> action)
{
return Add(new ActionOption<TKey, TValue>(prototype, description, action));
}
protected virtual OptionContext CreateOptionContext()
{
return new OptionContext(this);
}
public List<string> Parse(IEnumerable<string> arguments)
{
OptionContext optionContext = CreateOptionContext();
optionContext.OptionIndex = -1;
bool flag = true;
List<string> list = new List<string>();
Option def = Contains("<>") ? base["<>"] : null;
foreach (string argument in arguments)
{
optionContext.OptionIndex++;
if (argument == "--")
{
flag = false;
}
else if (!flag)
{
Unprocessed(list, def, optionContext, argument);
}
else if (!Parse(argument, optionContext))
{
Unprocessed(list, def, optionContext, argument);
}
}
if (optionContext.Option != null)
{
optionContext.Option.Invoke(optionContext);
}
return list;
}
private static bool Unprocessed(ICollection<string> extra, Option def, OptionContext c, string argument)
{
if (def == null)
{
extra.Add(argument);
return false;
}
c.OptionValues.Add(argument);
c.Option = def;
c.Option.Invoke(c);
return false;
}
protected bool GetOptionParts(string argument, out string flag, out string name, out string sep, out string value)
{
if (argument == null)
{
throw new ArgumentNullException("argument");
}
flag = (name = (sep = (value = null)));
Match match = ValueOption.Match(argument);
if (!match.Success)
{
return false;
}
flag = match.Groups["flag"].Value;
name = match.Groups["name"].Value;
if (match.Groups["sep"].Success && match.Groups["value"].Success)
{
sep = match.Groups["sep"].Value;
value = match.Groups["value"].Value;
}
return true;
}
protected virtual bool Parse(string argument, OptionContext c)
{
if (c.Option != null)
{
ParseValue(argument, c);
return true;
}
string flag;
string name;
string sep;
string value;
if (!GetOptionParts(argument, out flag, out name, out sep, out value))
{
return false;
}
if (Contains(name))
{
Option option = base[name];
c.OptionName = flag + name;
c.Option = option;
switch (option.OptionValueType)
{
case OptionValueType.None:
c.OptionValues.Add(name);
c.Option.Invoke(c);
break;
case OptionValueType.Optional:
case OptionValueType.Required:
ParseValue(value, c);
break;
}
return true;
}
if (ParseBool(argument, name, c))
{
return true;
}
if (ParseBundledValue(flag, string.Concat(name + sep + value), c))
{
return true;
}
return false;
}
private void ParseValue(string option, OptionContext c)
{
if (option != null)
{
string[] array = (c.Option.ValueSeparators != null) ? option.Split(c.Option.ValueSeparators, StringSplitOptions.None) : new string[1]
{
option
};
foreach (string item in array)
{
c.OptionValues.Add(item);
}
}
if (c.OptionValues.Count == c.Option.MaxValueCount || c.Option.OptionValueType == OptionValueType.Optional)
{
c.Option.Invoke(c);
}
else if (c.OptionValues.Count > c.Option.MaxValueCount)
{
throw new OptionException(localizer(string.Format("Error: Found {0} option values when expecting {1}.", c.OptionValues.Count, c.Option.MaxValueCount)), c.OptionName);
}
}
private bool ParseBool(string option, string n, OptionContext c)
{
string key;
if (n.Length >= 1 && (n[n.Length - 1] == '+' || n[n.Length - 1] == '-') && Contains(key = n.Substring(0, n.Length - 1)))
{
Option option2 = base[key];
string item = (n[n.Length - 1] == '+') ? option : null;
c.OptionName = option;
c.Option = option2;
c.OptionValues.Add(item);
option2.Invoke(c);
return true;
}
return false;
}
private bool ParseBundledValue(string f, string n, OptionContext c)
{
if (f != "-")
{
return false;
}
for (int i = 0; i < n.Length; i++)
{
string text = f + n[i].ToString();
string key = n[i].ToString();
if (!Contains(key))
{
if (i == 0)
{
return false;
}
throw new OptionException(string.Format(localizer("Cannot bundle unregistered option '{0}'."), text), text);
}
Option option = base[key];
switch (option.OptionValueType)
{
case OptionValueType.None:
break;
case OptionValueType.Optional:
case OptionValueType.Required:
{
string text2 = n.Substring(i + 1);
c.Option = option;
c.OptionName = text;
ParseValue((text2.Length != 0) ? text2 : null, c);
return true;
}
default:
throw new InvalidOperationException("Unknown OptionValueType: " + option.OptionValueType);
}
Invoke(c, text, n, option);
}
return true;
}
private static void Invoke(OptionContext c, string name, string value, Option option)
{
c.OptionName = name;
c.Option = option;
c.OptionValues.Add(value);
option.Invoke(c);
}
public void WriteOptionDescriptions(TextWriter o)
{
using (IEnumerator<Option> enumerator = GetEnumerator())
{
while (enumerator.MoveNext())
{
Option current = enumerator.Current;
int written = 0;
if (WriteOptionPrototype(o, current, ref written))
{
if (written < 29)
{
o.Write(new string(' ', 29 - written));
}
else
{
o.WriteLine();
o.Write(new string(' ', 29));
}
List<string> lines = GetLines(localizer(GetDescription(current.Description)));
o.WriteLine(lines[0]);
string value = new string(' ', 31);
for (int i = 1; i < lines.Count; i++)
{
o.Write(value);
o.WriteLine(lines[i]);
}
}
}
}
}
private bool WriteOptionPrototype(TextWriter o, Option p, ref int written)
{
string[] names = p.Names;
int nextOptionIndex = GetNextOptionIndex(names, 0);
if (nextOptionIndex == names.Length)
{
return false;
}
if (names[nextOptionIndex].Length == 1)
{
Write(o, ref written, " -");
Write(o, ref written, names[0]);
}
else
{
Write(o, ref written, " --");
Write(o, ref written, names[0]);
}
for (nextOptionIndex = GetNextOptionIndex(names, nextOptionIndex + 1); nextOptionIndex < names.Length; nextOptionIndex = GetNextOptionIndex(names, nextOptionIndex + 1))
{
Write(o, ref written, ", ");
Write(o, ref written, (names[nextOptionIndex].Length == 1) ? "-" : "--");
Write(o, ref written, names[nextOptionIndex]);
}
if (p.OptionValueType == OptionValueType.Optional || p.OptionValueType == OptionValueType.Required)
{
if (p.OptionValueType == OptionValueType.Optional)
{
Write(o, ref written, localizer("["));
}
Write(o, ref written, localizer("=" + GetArgumentName(0, p.MaxValueCount, p.Description)));
string str = (p.ValueSeparators != null && p.ValueSeparators.Length > 0) ? p.ValueSeparators[0] : " ";
for (int i = 1; i < p.MaxValueCount; i++)
{
Write(o, ref written, localizer(str + GetArgumentName(i, p.MaxValueCount, p.Description)));
}
if (p.OptionValueType == OptionValueType.Optional)
{
Write(o, ref written, localizer("]"));
}
}
return true;
}
private static int GetNextOptionIndex(string[] names, int i)
{
while (i < names.Length && names[i] == "<>")
{
i++;
}
return i;
}
private static void Write(TextWriter o, ref int n, string s)
{
n += s.Length;
o.Write(s);
}
private static string GetArgumentName(int index, int maxIndex, string description)
{
if (description == null)
{
if (maxIndex != 1)
{
return "VALUE" + (index + 1);
}
return "VALUE";
}
string[] array = (maxIndex != 1) ? new string[1]
{
"{" + index + ":"
} : new string[2]
{
"{0:",
"{"
};
for (int i = 0; i < array.Length; i++)
{
int num = 0;
int num2;
do
{
num2 = description.IndexOf(array[i], num);
}
while (num2 >= 0 && num != 0 && description[num++ - 1] == '{');
if (num2 != -1)
{
int num3 = description.IndexOf("}", num2);
if (num3 != -1)
{
return description.Substring(num2 + array[i].Length, num3 - num2 - array[i].Length);
}
}
}
if (maxIndex != 1)
{
return "VALUE" + (index + 1);
}
return "VALUE";
}
private static string GetDescription(string description)
{
if (description == null)
{
return string.Empty;
}
StringBuilder stringBuilder = new StringBuilder(description.Length);
int num = -1;
for (int i = 0; i < description.Length; i++)
{
switch (description[i])
{
case '{':
if (i == num)
{
stringBuilder.Append('{');
num = -1;
}
else if (num < 0)
{
num = i + 1;
}
continue;
case '}':
if (num < 0)
{
if (i + 1 == description.Length || description[i + 1] != '}')
{
throw new InvalidOperationException("Invalid option description: " + description);
}
i++;
stringBuilder.Append("}");
}
else
{
stringBuilder.Append(description.Substring(num, i - num));
num = -1;
}
continue;
case ':':
if (num >= 0)
{
num = i + 1;
continue;
}
break;
}
if (num < 0)
{
stringBuilder.Append(description[i]);
}
}
return stringBuilder.ToString();
}
private static List<string> GetLines(string description)
{
List<string> list = new List<string>();
if (string.IsNullOrEmpty(description))
{
list.Add(string.Empty);
return list;
}
int length = 49;
int num = 0;
int num2;
do
{
num2 = GetLineEnd(num, length, description);
bool flag = false;
if (num2 < description.Length)
{
char c = description[num2];
if (c == '-' || (char.IsWhiteSpace(c) && c != '\n'))
{
num2++;
}
else if (c != '\n')
{
flag = true;
num2--;
}
}
list.Add(description.Substring(num, num2 - num));
if (flag)
{
list[list.Count - 1] += "-";
}
num = num2;
if (num < description.Length && description[num] == '\n')
{
num++;
}
}
while (num2 < description.Length);
return list;
}
private static int GetLineEnd(int start, int length, string description)
{
int num = Math.Min(start + length, description.Length);
int num2 = -1;
for (int i = start; i < num; i++)
{
switch (description[i])
{
case '\t':
case '\v':
case ' ':
case ',':
case '-':
case '.':
case ';':
num2 = i;
break;
case '\n':
return i;
}
}
if (num2 == -1 || num == description.Length)
{
return num;
}
return num2;
}
}
}