24 using System.Collections.Generic;
25 using System.Collections.ObjectModel;
26 using System.ComponentModel;
27 using System.Diagnostics.CodeAnalysis;
29 using System.Runtime.Serialization;
30 using System.Security.Permissions;
32 using System.Text.RegularExpressions;
162 namespace Ntp.Common.IO
166 public static IEnumerable<string>
WrappedLines(
string self, params
int[] widths)
168 IEnumerable<int> w = widths;
169 return WrappedLines(
self, w);
172 public static IEnumerable<string>
WrappedLines(
string self, IEnumerable<int> widths)
175 throw new ArgumentNullException(nameof(widths));
176 return CreateWrappedLinesIterator(
self, widths);
181 if (
string.IsNullOrEmpty(
self))
183 yield
return string.Empty;
186 using (var ewidths = widths.GetEnumerator())
189 var width = GetNextWidth(ewidths,
int.MaxValue, ref hw);
193 var end = GetLineEnd(start, width,
self);
194 var
c =
self[end - 1];
195 if (
char.IsWhiteSpace(c))
197 var needContinuation = end !=
self.Length && !IsEolChar(c);
198 var continuation =
"";
199 if (needContinuation)
204 var line =
self.Substring(start, end - start) + continuation;
207 if (
char.IsWhiteSpace(c))
209 width = GetNextWidth(ewidths, width, ref hw);
210 }
while (start <
self.Length);
214 private static int GetLineEnd(
int start,
int length,
string description)
216 var end = Math.Min(start + length, description.Length);
218 for (var i = start; i < end; ++i)
220 if (description[i] ==
'\n')
222 if (IsEolChar(description[i]))
225 if (sep == -1 || end == description.Length)
230 private static int GetNextWidth(IEnumerator<int> ewidths,
int curWidth, ref
bool? eValid)
232 if (eValid.HasValue && !eValid.Value)
235 curWidth = (eValid = ewidths.MoveNext()).Value ? ewidths.Current : curWidth;
237 const string minWidth =
".-";
238 if (curWidth < minWidth.Length)
239 throw new ArgumentOutOfRangeException(nameof(curWidth),
240 $
"Element must be >= {minWidth.Length}, was {curWidth}.");
247 return !
char.IsLetterOrDigit(c);
260 private readonly List<string> values =
new List<string>();
264 IEnumerator IEnumerable.GetEnumerator()
266 return values.GetEnumerator();
271 #region IEnumerable<T> 275 return values.GetEnumerator();
282 return values.ToArray();
287 return new List<string>(values);
292 return string.Join(
", ", values.ToArray());
297 void ICollection.CopyTo(Array array,
int index)
299 (values as ICollection).CopyTo(array, index);
302 bool ICollection.IsSynchronized => (values as ICollection).IsSynchronized;
303 object ICollection.SyncRoot => (values as ICollection).SyncRoot;
307 #region ICollection<T> 309 public void Add(
string item)
321 return values.Contains(item);
324 public void CopyTo(
string[] array,
int arrayIndex)
326 values.CopyTo(array, arrayIndex);
331 return values.Remove(item);
334 public int Count => values.Count;
335 public bool IsReadOnly =>
false;
341 int IList.Add(
object value)
343 return (values as IList).Add(value);
346 bool IList.Contains(
object value)
348 return (values as IList).Contains(value);
351 int IList.IndexOf(
object value)
353 return (values as IList).IndexOf(value);
356 void IList.Insert(
int index,
object value)
358 (values as IList).Insert(index, value);
361 void IList.Remove(
object value)
363 (values as IList).Remove(value);
366 void IList.RemoveAt(
int index)
368 (values as IList).RemoveAt(index);
371 bool IList.IsFixedSize =>
false;
373 object IList.this[
int index]
375 get {
return this[index]; }
376 set { (values as IList)[index] = value; }
385 return values.IndexOf(item);
388 public void Insert(
int index,
string item)
390 values.Insert(index, item);
395 values.RemoveAt(index);
400 if (c.Option == null)
401 throw new InvalidOperationException(
"OptionContext.Option is null.");
402 if (index >= c.Option.MaxValueCount)
403 throw new ArgumentOutOfRangeException(nameof(index));
405 index >= values.Count)
407 c.OptionSet.MessageLocalizer(
"Missing required value for option '{0}'."), c.OptionName),
411 public string this[
int index]
416 return index >= values.Count ? null : values[index];
418 set { values[index] = value; }
434 public string OptionName {
get;
set; }
436 public int OptionIndex {
get;
set; }
452 protected Option(
string prototype,
string description)
453 : this(prototype, description, 1, false)
457 protected Option(
string prototype,
string description,
int maxValueCount)
458 : this(prototype, description, maxValueCount, false)
462 [SuppressMessage(
"Microsoft.Usage",
"CA2214:DoNotCallOverridableMethodsInConstructors")]
463 protected Option(
string prototype,
string description,
int maxValueCount,
bool hidden)
465 if (prototype == null)
466 throw new ArgumentNullException(nameof(prototype));
467 if (prototype.Length == 0)
468 throw new ArgumentException(
"Cannot be the empty string.", nameof(prototype));
469 if (maxValueCount < 0)
470 throw new ArgumentOutOfRangeException(nameof(maxValueCount));
472 Prototype = prototype;
473 Description = description;
474 MaxValueCount = maxValueCount;
479 ?
new[] {prototype + GetHashCode()}
480 : prototype.Split(
'|');
489 throw new ArgumentException(
490 "Cannot provide maxValueCount of 0 for OptionValueType.Required or " +
491 "OptionValueType.Optional.",
492 nameof(maxValueCount));
495 throw new ArgumentException(
496 $
"Cannot provide maxValueCount of {maxValueCount} for OptionValueType.None.",
497 nameof(maxValueCount));
499 if (Array.IndexOf(Names,
"<>") >= 0 &&
501 (Names.Length > 1 && MaxValueCount > 1)))
502 throw new ArgumentException(
503 "The default option handler '<>' cannot require values.",
507 private static readonly
char[] NameTerminator = {
'=',
':'};
509 public string Prototype {
get; }
511 public string Description {
get; }
515 public int MaxValueCount {
get; }
517 public bool Hidden {
get; }
519 internal string[] Names {
get; }
521 internal string[] ValueSeparators {
get;
private set; }
525 return (
string[]) Names.Clone();
530 if (ValueSeparators == null)
531 return new string[0];
532 return (
string[]) ValueSeparators.Clone();
558 !ti.IsGenericTypeDefinition &&
559 ti.GetGenericTypeDefinition() == typeof(Nullable<>);
561 var targetType = nullable ? tt.GetGenericArguments()[0] : tt;
567 var conv = TypeDescriptor.GetConverter(targetType);
568 t = (T) conv.ConvertFromString(value);
582 private static void AddSeparators(
string name,
int end, ICollection<string> seps)
585 for (var i = end + 1; i < name.Length; ++i)
591 throw new ArgumentException($
"Ill-formed name/value separator found in \"{name}\".");
596 throw new ArgumentException($
"Ill-formed name/value separator found in \"{name}\".");
597 seps.Add(name.Substring(start, i - start));
602 seps.Add(name[i].ToString());
608 throw new ArgumentException($
"Ill-formed name/value separator found in \"{name}\".");
614 var seps =
new List<string>();
616 for (var i = 0; i < Names.Length; ++i)
619 if (name.Length == 0)
620 throw new ArgumentException(
"Empty option names are not supported.");
622 var end = name.IndexOfAny(NameTerminator);
625 Names[i] = name.Substring(0, end);
626 if (type ==
'\0' || type == name[end])
629 throw new ArgumentException($
"Conflicting option types: '{type}' vs. '{name[end]}'.");
630 AddSeparators(name, end, seps);
636 if (MaxValueCount <= 1 && seps.Count != 0)
637 throw new ArgumentException(
638 $
"Cannot provide key/value separators for Options taking {MaxValueCount} value(s).");
640 if (MaxValueCount > 1)
643 ValueSeparators =
new[] {
":",
"="};
644 else if (seps.Count == 1 && seps[0].Length == 0)
645 ValueSeparators = null;
647 ValueSeparators = seps.ToArray();
656 public abstract string Description {
get; }
657 public abstract bool GetArguments(
string value, out IEnumerable<string> replacement);
661 return GetArguments(reader,
false);
666 return GetArguments(File.OpenText(file),
true);
669 public abstract string[] GetNames();
676 var arg =
new StringBuilder();
679 while ((line = reader.ReadLine()) != null)
683 for (var i = 0; i < t; i++)
687 if (c ==
'"' || c ==
'\'')
691 for (i++; i < t; i++)
704 yield
return arg.ToString();
713 yield
return arg.ToString();
728 public override string Description =>
"Read response file for more options.";
730 public override bool GetArguments(
string value, out IEnumerable<string> replacement)
732 if (
string.IsNullOrEmpty(value) || !value.StartsWith(
"@"))
737 replacement = GetArgumentsFromFile(value.Substring(1));
743 return new[] {
"@file"};
757 OptionName = optionName;
761 : base(message, innerException)
763 OptionName = optionName;
767 : base(info, context)
769 OptionName = info.GetString(
"OptionName");
772 public string OptionName {
get; }
773 #pragma warning disable 618 // SecurityPermissionAttribute is obsolete 774 [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter =
true)]
775 #pragma warning restore 618 776 public override void GetObjectData(SerializationInfo info, StreamingContext context)
778 base.GetObjectData(info, context);
779 info.AddValue(
"OptionName", OptionName);
785 public class OptionSet : KeyedCollection<string, Option>
794 MessageLocalizer = localizer;
795 ArgumentSources =
new ReadOnlyCollection<ArgumentSource>(sources);
798 private const int OptionWidth = 29;
799 private const int DescriptionFirstWidth = 80 - OptionWidth;
800 private const int DescriptionRemWidth = 80 - OptionWidth - 2;
802 private readonly List<ArgumentSource> sources =
new List<ArgumentSource>();
804 private readonly Regex valueOption =
new Regex(
805 @"^(?<flag>--|-|/)(?<name>[^:=]+)((?<sep>[:=])(?<value>.*))?$");
809 public ReadOnlyCollection<ArgumentSource> ArgumentSources {
get; }
814 throw new ArgumentNullException(nameof(header));
828 return Add(prototype, null, action);
831 public OptionSet Add(
string prototype,
string description, Action<string> action)
833 return Add(prototype, description, action,
false);
836 public OptionSet Add(
string prototype,
string description, Action<string> action,
bool hidden)
839 throw new ArgumentNullException(nameof(action));
846 public OptionSet Add(
string prototype, OptionAction<string, string> action)
848 return Add(prototype, null, action);
851 public OptionSet Add(
string prototype,
string description, OptionAction<string, string> action)
853 return Add(prototype, description, action,
false);
856 public OptionSet Add(
string prototype,
string description, OptionAction<string, string> action,
bool hidden)
859 throw new ArgumentNullException(nameof(action));
866 public OptionSet Add<T>(
string prototype, Action<T> action)
868 return Add(prototype, null, action);
871 public OptionSet Add<T>(
string prototype,
string description, Action<T> action)
876 public OptionSet Add<TKey, TValue>(
string prototype, OptionAction<TKey, TValue> action)
878 return Add(prototype, null, action);
881 public OptionSet Add<TKey, TValue>(
string prototype,
string description, OptionAction<TKey, TValue> action)
889 throw new ArgumentNullException(nameof(source));
894 public List<string>
Parse(IEnumerable<string> arguments)
896 if (arguments == null)
897 throw new ArgumentNullException(nameof(arguments));
899 var c = CreateOptionContext();
902 var unprocessed =
new List<string>();
903 var def = Contains(
"<>") ?
this[
"<>"] : null;
906 foreach (var argument
in ae)
909 if (argument ==
"--")
916 Unprocessed(unprocessed, def, c, argument);
919 if (AddSource(ae, argument))
921 if (!Parse(argument, c))
922 Unprocessed(unprocessed, def, c, argument);
931 foreach (var p
in this)
941 WriteDescription(o, p.Description,
"", 80, 80);
945 if (!WriteOptionPrototype(o, p, ref written))
948 if (written < OptionWidth)
949 o.Write(
new string(
' ', OptionWidth - written));
953 o.Write(
new string(
' ', OptionWidth));
956 WriteDescription(o, p.Description,
new string(
' ', OptionWidth + 2),
957 DescriptionFirstWidth, DescriptionRemWidth);
960 foreach (var
s in sources)
962 var names = s.GetNames();
963 if (names == null || names.Length == 0)
968 Write(o, ref written,
" ");
969 Write(o, ref written, names[0]);
970 for (var i = 1; i < names.Length; ++i)
972 Write(o, ref written,
", ");
973 Write(o, ref written, names[i]);
976 if (written < OptionWidth)
977 o.Write(
new string(
' ', OptionWidth - written));
981 o.Write(
new string(
' ', OptionWidth));
984 WriteDescription(o, s.Description,
new string(
' ', OptionWidth + 2),
985 DescriptionFirstWidth, DescriptionRemWidth);
998 throw new ArgumentNullException(nameof(item));
999 if (item.
Names != null && item.
Names.Length > 0)
1000 return item.
Names[0];
1003 throw new InvalidOperationException(
"Option has no names!");
1006 [Obsolete(
"Use KeyedCollection.this[string]")]
1010 throw new ArgumentNullException(nameof(option));
1013 return base[option];
1015 catch (KeyNotFoundException)
1021 protected bool GetOptionParts(
string argument, out
string flag, out
string name, out
string sep,
1024 if (argument == null)
1025 throw new ArgumentNullException(nameof(argument));
1027 flag = name = sep = value = null;
1028 var m = valueOption.Match(argument);
1033 flag = m.Groups[
"flag"].Value;
1034 name = m.Groups[
"name"].Value;
1035 if (m.Groups[
"sep"].Success && m.Groups[
"value"].Success)
1037 sep = m.Groups[
"sep"].Value;
1038 value = m.Groups[
"value"].Value;
1045 base.InsertItem(index, item);
1053 ParseValue(argument, c);
1058 if (!GetOptionParts(argument, out f, out n, out s, out v))
1066 switch (p.OptionValueType)
1081 return ParseBool(argument, n, c) || ParseBundledValue(f, $
"{n}{s}{v}", c);
1086 var p = Items[index];
1087 base.RemoveItem(index);
1089 for (var i = 1; i < p.Names.Length; ++i)
1091 Dictionary.Remove(p.Names[i]);
1097 base.SetItem(index, item);
1104 throw new ArgumentNullException(nameof(option));
1105 var added =
new List<string>(option.
Names.Length);
1109 for (var i = 1; i < option.
Names.Length; ++i)
1111 Dictionary.Add(option.
Names[i], option);
1112 added.Add(option.
Names[i]);
1117 foreach (var name
in added)
1118 Dictionary.Remove(name);
1125 foreach (var source
in sources)
1127 IEnumerable<string> replacement;
1128 if (!source.GetArguments(argument, out replacement))
1130 ae.Add(replacement);
1138 if (description == null)
1139 return maxIndex == 1 ?
"VALUE" :
"VALUE" + (index + 1);
1141 var nameStart = maxIndex == 1 ?
new[] {
"{0:",
"{"} :
new[] {
"{" + index +
":"};
1143 foreach (var t
in nameStart)
1148 start = description.IndexOf(t, j, StringComparison.Ordinal);
1149 }
while (start >= 0 && j != 0 && description[j++ - 1] ==
'{');
1152 var end = description.IndexOf(
"}", start, StringComparison.Ordinal);
1155 return description.Substring(start + t.Length, end - start - t.Length);
1158 return maxIndex == 1 ?
"VALUE" :
"VALUE" + (index + 1);
1163 if (description == null)
1164 return string.Empty;
1165 var sb =
new StringBuilder(description.Length);
1167 for (var i = 0; i < description.Length; ++i)
1169 switch (description[i])
1183 if (i + 1 == description.Length || description[i + 1] !=
'}')
1184 throw new InvalidOperationException(
"Invalid option description: " + description);
1190 sb.Append(description.Substring(start, i - start));
1201 sb.Append(description[i]);
1205 return sb.ToString();
1208 private static IEnumerable<string>
GetLines(
string description,
int firstWidth,
int remWidth)
1215 while (i < names.Length && names[i] ==
"<>")
1233 if (n.Length >= 1 && (n[n.Length - 1] ==
'+' || n[n.Length - 1] ==
'-') &&
1234 Contains(rn = n.Substring(0, n.Length - 1)))
1237 var v = n[n.Length - 1] ==
'+' ? option : null;
1251 for (var i = 0; i < n.Length; ++i)
1254 var rn = n[i].ToString();
1260 "Cannot use unregistered option '{0}' in bundle '{1}'."), rn, f + n), null);
1263 switch (p.OptionValueType)
1266 Invoke(c, opt, n, p);
1271 var v = n.Substring(i + 1);
1274 ParseValue(v.Length != 0 ? v : null, c);
1278 throw new InvalidOperationException(
"Unknown OptionValueType: " + p.OptionValueType);
1287 foreach (var o
in c.Option.ValueSeparators != null
1288 ? option.Split(c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count,
1289 StringSplitOptions.None)
1292 c.OptionValues.Add(o);
1294 if (c.OptionValues.Count == c.Option.MaxValueCount ||
1297 else if (c.OptionValues.Count > c.Option.MaxValueCount)
1300 $
"Error: Found {c.OptionValues.Count} option values when expecting {c.Option.MaxValueCount}."),
1309 extra.Add(argument);
1317 private static void Write(TextWriter o, ref
int n,
string s)
1323 private void WriteDescription(TextWriter o,
string value,
string prefix,
int firstWidth,
int remWidth)
1326 foreach (var line
in GetLines(MessageLocalizer(GetDescription(value)), firstWidth, remWidth))
1337 var names = p.
Names;
1339 var i = GetNextOptionIndex(names, 0);
1340 if (i == names.Length)
1343 if (names[i].Length == 1)
1345 Write(o, ref written,
" -");
1346 Write(o, ref written, names[0]);
1350 Write(o, ref written,
" --");
1351 Write(o, ref written, names[0]);
1354 for (i = GetNextOptionIndex(names, i + 1);
1356 i = GetNextOptionIndex(names, i + 1))
1358 Write(o, ref written,
", ");
1359 Write(o, ref written, names[i].Length == 1 ?
"-" :
"--");
1360 Write(o, ref written, names[i]);
1368 Write(o, ref written, MessageLocalizer(
"["));
1380 Write(o, ref written, MessageLocalizer(
"]"));
1392 : base(
"=:Category:= " + description, description)
1398 throw new NotSupportedException(
"Category.OnParseComplete should not be invoked.");
1404 public ActionOption(
string prototype,
string description,
int count, Action<OptionValueCollection> action,
1406 : base(prototype, description, count, hidden)
1409 throw new ArgumentNullException(nameof(action));
1410 this.action = action;
1413 private readonly Action<OptionValueCollection>
action;
1423 public ActionOption(
string prototype,
string description, Action<T> action)
1424 : base(prototype, description, 1)
1427 throw new ArgumentNullException(nameof(action));
1428 this.action = action;
1441 public ActionOption(
string prototype,
string description, OptionAction<TKey, TValue> action)
1442 : base(prototype, description, 2)
1445 throw new ArgumentNullException(nameof(action));
1446 this.action = action;
1449 private readonly OptionAction<TKey, TValue>
action;
1463 sources.Add(arguments.GetEnumerator());
1466 private readonly List<IEnumerator<string>> sources =
new List<IEnumerator<string>>();
1472 var c = sources[sources.Count - 1];
1474 yield
return c.Current;
1478 sources.RemoveAt(sources.Count - 1);
1480 }
while (sources.Count > 0);
1483 IEnumerator IEnumerable.GetEnumerator()
1485 return GetEnumerator();
1488 public void Add(IEnumerable<string> arguments)
1490 sources.Add(arguments.GetEnumerator());
static int GetNextOptionIndex(string[] names, int i)
bool WriteOptionPrototype(TextWriter o, Option p, ref int written)
string[] GetValueSeparators()
OptionValueType ParsePrototype()
readonly OptionAction< TKey, TValue > action
OptionValueType OptionValueType
static string GetDescription(string description)
IEnumerator< string > GetEnumerator()
IEnumerator< string > GetEnumerator()
static void Unprocessed(ICollection< string > extra, Option def, OptionContext c, string argument)
void AddImpl(Option option)
OptionSet Add(ArgumentSource source)
OptionSet Add(string header)
Option GetOptionForName(string option)
static void Invoke(OptionContext c, string name, string value, Option option)
ActionOption(string prototype, string description, int count, Action< OptionValueCollection > action, bool hidden)
void Insert(int index, string item)
static IEnumerable< string > GetArguments(TextReader reader, bool close)
override string ToString()
static IEnumerable< string > GetLines(string description, int firstWidth, int remWidth)
if(typeof jQuery=== 'undefined')
static string GetArgumentName(int index, int maxIndex, string description)
static int GetLineEnd(int start, int length, string description)
bool ParseBool(string option, string n, OptionContext c)
ActionOption(string prototype, string description, Action< T > action)
void CopyTo(string[] array, int arrayIndex)
bool GetOptionParts(string argument, out string flag, out string name, out string sep, out string value)
ArgumentEnumerator(IEnumerable< string > arguments)
OptionSet Add(string prototype, string description, OptionAction< string, string > action, bool hidden)
Option(string prototype, string description, int maxValueCount)
override string ToString()
Option(string prototype, string description, int maxValueCount, bool hidden)
void WriteOptionDescriptions(TextWriter o)
virtual bool Parse(string argument, OptionContext c)
OptionSet Add(string prototype, string description, Action< string > action, bool hidden)
static IEnumerable< string > WrappedLines(string self, IEnumerable< int > widths)
Category(string description)
static IEnumerable< string > GetArgumentsFromFile(string file)
OptionException(string message, string optionName, Exception innerException)
virtual OptionContext CreateOptionContext()
OptionSet Add(string prototype, Action< string > action)
readonly Action< OptionValueCollection > action
Option(string prototype, string description)
static IEnumerable< string > GetArguments(TextReader reader)
bool AddSource(ArgumentEnumerator ae, string argument)
OptionException(string message, string optionName)
void Invoke(OptionContext c)
override void SetItem(int index, Option item)
static void AddSeparators(string name, int end, ICollection< string > seps)
override void OnParseComplete(OptionContext c)
OptionSet Add(string prototype, string description, OptionAction< string, string > action)
static void Write(TextWriter o, ref int n, string s)
new OptionSet Add(Option option)
void WriteDescription(TextWriter o, string value, string prefix, int firstWidth, int remWidth)
bool ParseBundledValue(string f, string n, OptionContext c)
override void GetObjectData(SerializationInfo info, StreamingContext context)
override string[] GetNames()
void AssertValid(int index)
OptionContext(OptionSet set)
static int GetNextWidth(IEnumerator< int > ewidths, int curWidth, ref bool?eValid)
override void OnParseComplete(OptionContext c)
override void RemoveItem(int index)
OptionSet(MessageLocalizerConverter localizer)
override string GetKeyForItem(Option item)
OptionSet Add(string prototype, string description, Action< string > action)
ActionOption(string prototype, string description, OptionAction< TKey, TValue > action)
OptionValueCollection(OptionContext c)
override bool GetArguments(string value, out IEnumerable< string > replacement)
delegate void OptionAction< in TKey, in TValue >(TKey key, TValue value)
OptionValueCollection OptionValues
override void InsertItem(int index, Option item)
MessageLocalizerConverter MessageLocalizer
List< string > Parse(IEnumerable< string > arguments)
void Add(IEnumerable< string > arguments)
static bool IsEolChar(char c)
readonly Action< T > action
bool Contains(string item)
OptionSet Add(string prototype, OptionAction< string, string > action)
static IEnumerable< string > WrappedLines(string self, params int[] widths)
static IEnumerable< string > CreateWrappedLinesIterator(string self, IEnumerable< int > widths)
OptionException(SerializationInfo info, StreamingContext context)
void ParseValue(string option, OptionContext c)