vxodbc: render timeless datetimes correctly into strings.
[versaplex.git] / wvdotnet / ndesk-options.cs
blob05692d311e6785ee12f55502ce46494251be02fd
1 //
2 // Options.cs
3 //
4 // Authors:
5 // Jonathan Pryor <jpryor@novell.com>
6 //
7 // Copyright (C) 2008 Novell (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 // Compile With:
30 // gmcs -debug+ -d:TEST -r:System.Core Options.cs
31 // gmcs -debug+ -d:LINQ -d:TEST -r:System.Core Options.cs
34 // A Getopt::Long-inspired option parsing library for C#.
36 // NDesk.Options.OptionSet is built upon a key/value table, where the
37 // key is a option format string and the value is an Action<string>
38 // delegate that is invoked when the format string is matched.
40 // Option format strings:
41 // BNF Grammar: ( name [=:]? ) ( '|' name [=:]? )+
42 //
43 // Each '|'-delimited name is an alias for the associated action. If the
44 // format string ends in a '=', it has a required value. If the format
45 // string ends in a ':', it has an optional value. If neither '=' or ':'
46 // is present, no value is supported.
48 // Options are extracted either from the current option by looking for
49 // the option name followed by an '=' or ':', or is taken from the
50 // following option IFF:
51 // - The current option does not contain a '=' or a ':'
52 // - The following option is not a registered named option
54 // The `name' used in the option format string does NOT include any leading
55 // option indicator, such as '-', '--', or '/'. All three of these are
56 // permitted/required on any named option.
58 // Option bundling is permitted so long as:
59 // - '-' is used to start the option group
60 // - all of the bundled options do not require values
61 // - all of the bundled options are a single character
63 // This allows specifying '-a -b -c' as '-abc'.
65 // Option processing is disabled by specifying "--". All options after "--"
66 // are returned by OptionSet.Parse() unchanged and unprocessed.
68 // Unprocessed options are returned from OptionSet.Parse().
70 // Examples:
71 // int verbose = 0;
72 // OptionSet p = new OptionSet ()
73 // .Add ("v", v => ++verbose)
74 // .Add ("name=|value=", v => Console.WriteLine (v));
75 // p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"});
77 // The above would parse the argument string array, and would invoke the
78 // lambda expression three times, setting `verbose' to 3 when complete.
79 // It would also print out "A" and "B" to standard output.
80 // The returned array would contain the string "extra".
82 // C# 3.0 collection initializers are supported:
83 // var p = new OptionSet () {
84 // { "h|?|help", v => ShowHelp () },
85 // };
87 // System.ComponentModel.TypeConverter is also supported, allowing the use of
88 // custom data types in the callback type; TypeConverter.ConvertFromString()
89 // is used to convert the value option to an instance of the specified
90 // type:
92 // var p = new OptionSet () {
93 // { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) },
94 // };
96 // Random other tidbits:
97 // - Boolean options (those w/o '=' or ':' in the option format string)
98 // are explicitly enabled if they are followed with '+', and explicitly
99 // disabled if they are followed with '-':
100 // string a = null;
101 // var p = new OptionSet () {
102 // { "a", s => a = s },
103 // };
104 // p.Parse (new string[]{"-a"}); // sets v != null
105 // p.Parse (new string[]{"-a+"}); // sets v != null
106 // p.Parse (new string[]{"-a-"}); // sets v == null
108 #define LINQ
110 using System;
111 using System.Collections;
112 using System.Collections.Generic;
113 using System.Collections.ObjectModel;
114 using System.ComponentModel;
115 using System.Globalization;
116 using System.IO;
117 using System.Runtime.Serialization;
118 using System.Text.RegularExpressions;
121 #if LINQ
122 using System.Linq;
123 #endif
125 #if TEST
126 using Wv.NDesk.Options;
127 #endif
129 #if !LINQ
130 namespace System {
131 public delegate void Action<T1,T2> (T1 a, T2 b);
133 #endif
135 namespace Wv.NDesk.Options {
137 public enum OptionValueType {
138 None,
139 Optional,
140 Required,
143 public class OptionContext {
144 public OptionContext ()
148 public Option Option { get; set; }
149 public string OptionName { get; set; }
150 public int OptionIndex { get; set; }
151 public string OptionValue { get; set; }
154 public abstract class Option {
155 string prototype, description;
156 string[] names;
157 OptionValueType type;
159 protected Option (string prototype, string description)
161 if (prototype == null)
162 throw new ArgumentNullException ("prototype");
163 if (prototype.Length == 0)
164 throw new ArgumentException ("Cannot be the empty string.", "prototype");
166 this.prototype = prototype;
167 this.names = prototype.Split ('|');
168 this.description = description;
169 this.type = ValidateNames ();
172 public string Prototype { get { return prototype; } }
173 public string Description { get { return description; } }
174 public OptionValueType OptionValueType { get { return type; } }
176 public string[] GetNames ()
178 return (string[]) names.Clone ();
181 internal string[] Names { get { return names; } }
183 static readonly char[] NameTerminator = new char[]{'=', ':'};
184 private OptionValueType ValidateNames ()
186 char type = '\0';
187 for (int i = 0; i < names.Length; ++i) {
188 string name = names [i];
189 if (name.Length == 0)
190 throw new ArgumentException ("Empty option names are not supported.", "prototype");
192 int end = name.IndexOfAny (NameTerminator);
193 if (end > 0) {
194 names [i] = name.Substring (0, end);
195 if (type == '\0' || type == name [end])
196 type = name [end];
197 else
198 throw new ArgumentException (
199 string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]),
200 "prototype");
203 if (type == '\0')
204 return OptionValueType.None;
205 return type == '=' ? OptionValueType.Required : OptionValueType.Optional;
208 public void Invoke (OptionContext c)
210 OnParseComplete (c);
211 c.OptionName = null;
212 c.OptionValue = null;
213 c.Option = null;
216 protected abstract void OnParseComplete (OptionContext c);
218 public override string ToString ()
220 return Prototype;
224 [Serializable]
225 public class OptionException : Exception {
226 private string option;
228 public OptionException (string message, string optionName)
229 : base (message)
231 this.option = optionName;
234 public OptionException (string message, string optionName, Exception innerException)
235 : base (message, innerException)
237 this.option = optionName;
240 protected OptionException (SerializationInfo info, StreamingContext context)
241 : base (info, context)
243 this.option = info.GetString ("OptionName");
246 public string OptionName {
247 get {return this.option;}
250 public override void GetObjectData (SerializationInfo info, StreamingContext context)
252 base.GetObjectData (info, context);
253 info.AddValue ("OptionName", option);
257 public class OptionSet : Collection<Option>
259 public OptionSet ()
260 : this (f => f)
264 public OptionSet (Converter<string, string> localizer)
266 this.localizer = localizer;
269 Dictionary<string, Option> options = new Dictionary<string, Option> ();
270 Converter<string, string> localizer;
272 protected Option GetOptionForName (string option)
274 if (option == null)
275 throw new ArgumentNullException ("option");
276 Option v;
277 if (options.TryGetValue (option, out v))
278 return v;
279 return null;
282 protected override void ClearItems ()
284 this.options.Clear ();
287 protected override void InsertItem (int index, Option item)
289 Add (item);
290 base.InsertItem (index, item);
293 protected override void RemoveItem (int index)
295 Option p = Items [index];
296 foreach (string name in p.Names) {
297 this.options.Remove (name);
299 base.RemoveItem (index);
302 protected override void SetItem (int index, Option item)
304 RemoveItem (index);
305 Add (item);
306 base.SetItem (index, item);
309 class ActionOption : Option {
310 Action<string, OptionContext> action;
312 public ActionOption (string prototype, string description, Action<string, OptionContext> action)
313 : base (prototype, description)
315 if (action == null)
316 throw new ArgumentNullException ("action");
317 this.action = action;
320 protected override void OnParseComplete (OptionContext c)
322 action (c.OptionValue, c);
326 public new OptionSet Add (Option option)
328 if (option == null)
329 throw new ArgumentNullException ("option");
330 List<string> added = new List<string> ();
331 try {
332 foreach (string name in option.Names) {
333 this.options.Add (name, option);
336 catch (Exception) {
337 foreach (string name in added)
338 this.options.Remove (name);
339 throw;
341 return this;
344 public OptionSet Add (string options, Action<string> action)
346 return Add (options, null, action);
349 public OptionSet Add (string options, Action<string, OptionContext> action)
351 return Add (options, null, action);
354 public OptionSet Add (string options, string description, Action<string> action)
356 if (action == null)
357 throw new ArgumentNullException ("action");
358 return Add (options, description, (v,c) => {action (v);});
361 public OptionSet Add (string options, string description, Action<string, OptionContext> action)
363 Option p = new ActionOption (options, description, action);
364 base.Add (p);
365 return this;
368 public OptionSet Add<T> (string options, Action<T> action)
370 return Add (options, null, action);
373 public OptionSet Add<T> (string options, Action<T, OptionContext> action)
375 return Add (options, null, action);
378 public OptionSet Add<T> (string options, string description, Action<T> action)
380 return Add (options, description, (T v, OptionContext c) => {action (v);});
383 public OptionSet Add<T> (string options, string description, Action<T, OptionContext> action)
385 TypeConverter conv = TypeDescriptor.GetConverter (typeof(T));
386 Action<string, OptionContext> a = delegate (string s, OptionContext c) {
387 T t = default(T);
388 try {
389 if (s != null)
390 t = (T) conv.ConvertFromString (s);
392 catch (Exception e) {
393 throw new OptionException (
394 string.Format (
395 localizer ("Could not convert string `{0}' to type {1} for option `{2}'."),
396 s, typeof(T).Name, c.OptionName),
397 c.OptionName, e);
399 action (t, c);
401 return Add (options, description, a);
404 protected virtual OptionContext CreateOptionContext ()
406 return new OptionContext ();
409 #if LINQ
410 public List<string> Parse (IEnumerable<string> options)
412 bool process = true;
413 OptionContext c = CreateOptionContext ();
414 c.OptionIndex = -1;
415 var unprocessed =
416 from option in options
417 where ++c.OptionIndex >= 0 && process
418 ? option == "--"
419 ? (process = false)
420 : !Parse (option, c)
421 : true
422 select option;
423 List<string> r = unprocessed.ToList ();
424 if (c.Option != null)
425 NoValue (c);
426 return r;
428 #else
429 public List<string> Parse (IEnumerable<string> options)
431 OptionContext c = CreateOptionContext ();
432 c.OptionIndex = -1;
433 bool process = true;
434 List<string> unprocessed = new List<string> ();
435 foreach (string option in options) {
436 ++c.OptionIndex;
437 if (option == "--") {
438 process = false;
439 continue;
441 if (!process) {
442 unprocessed.Add (option);
443 continue;
445 if (!Parse (option, c))
446 unprocessed.Add (option);
448 if (c.Option != null)
449 NoValue (c);
450 return unprocessed;
452 #endif
454 private readonly Regex ValueOption = new Regex (
455 @"^(?<flag>--|-|/)(?<name>[^:=]+)([:=](?<value>.*))?$");
457 protected bool GetOptionParts (string option, out string flag, out string name, out string value)
459 Match m = ValueOption.Match (option);
460 if (!m.Success) {
461 flag = name = value = null;
462 return false;
464 flag = m.Groups ["flag"].Value;
465 name = m.Groups ["name"].Value;
466 value = !m.Groups ["value"].Success ? null : m.Groups ["value"].Value;
467 return true;
470 protected virtual bool Parse (string option, OptionContext c)
472 if (c.Option != null) {
473 c.OptionValue = option;
474 c.Option.Invoke (c);
475 return true;
478 string f, n, v;
479 if (!GetOptionParts (option, out f, out n, out v))
480 return false;
482 Option p;
483 if (this.options.TryGetValue (n, out p)) {
484 c.OptionName = f + n;
485 c.Option = p;
486 switch (p.OptionValueType) {
487 case OptionValueType.None:
488 c.OptionValue = n;
489 c.Option.Invoke (c);
490 break;
491 case OptionValueType.Optional:
492 case OptionValueType.Required:
493 if (v != null) {
494 c.OptionValue = v;
495 c.Option.Invoke (c);
497 break;
499 return true;
501 // no match; is it a bool option?
502 if (ParseBool (option, n, c))
503 return true;
504 // is it a bundled option?
505 if (ParseBundled (f, n, c))
506 return true;
508 return false;
511 private bool ParseBool (string option, string n, OptionContext c)
513 Option p;
514 if (n.Length >= 1 && (n [n.Length-1] == '+' || n [n.Length-1] == '-') &&
515 this.options.TryGetValue (n.Substring (0, n.Length-1), out p)) {
516 string v = n [n.Length-1] == '+' ? option : null;
517 c.OptionName = option;
518 c.OptionValue = v;
519 c.Option = p;
520 p.Invoke (c);
521 return true;
523 return false;
526 private bool ParseBundled (string f, string n, OptionContext c)
528 Option p;
529 if (f == "-" && this.options.TryGetValue (n [0].ToString (), out p)) {
530 int i = 0;
531 do {
532 string opt = "-" + n [i].ToString ();
533 if (p.OptionValueType != OptionValueType.None) {
534 throw new OptionException (string.Format (
535 localizer ("Cannot bundle option '{0}' that requires a value."), opt),
536 opt);
538 c.OptionName = opt;
539 c.OptionValue = n;
540 c.Option = p;
541 p.Invoke (c);
542 } while (++i < n.Length && this.options.TryGetValue (n [i].ToString (), out p));
543 return true;
545 return false;
548 private void NoValue (OptionContext c)
550 c.OptionValue = null;
551 Option p = c.Option;
552 if (p != null && p.OptionValueType == OptionValueType.Optional) {
553 p.Invoke (c);
555 else if (p != null && p.OptionValueType == OptionValueType.Required) {
556 throw new OptionException (string.Format (
557 localizer ("Missing required value for option '{0}'."), c.OptionName),
558 c.OptionName);
562 private const int OptionWidth = 29;
564 public void WriteOptionDescriptions (TextWriter o)
566 foreach (Option p in this) {
567 List<string> names = new List<string> (p.Names);
569 int written = 0;
570 if (names [0].Length == 1) {
571 Write (o, ref written, " -");
572 Write (o, ref written, names [0]);
574 else {
575 Write (o, ref written, " --");
576 Write (o, ref written, names [0]);
579 for (int i = 1; i < names.Count; ++i) {
580 Write (o, ref written, ", ");
581 Write (o, ref written, names [i].Length == 1 ? "-" : "--");
582 Write (o, ref written, names [i]);
585 if (p.OptionValueType == OptionValueType.Optional)
586 Write (o, ref written, localizer ("[=VALUE]"));
587 else if (p.OptionValueType == OptionValueType.Required)
588 Write (o, ref written, localizer ("=VALUE"));
590 if (written < OptionWidth)
591 o.Write (new string (' ', OptionWidth - written));
592 else {
593 o.WriteLine ();
594 o.Write (new string (' ', OptionWidth));
597 o.WriteLine (localizer (p.Description));
601 static void Write (TextWriter o, ref int n, string s)
603 n += s.Length;
604 o.Write (s);
609 #if TEST
610 namespace Tests.NDesk.Options {
612 using System.Linq;
614 class FooConverter : TypeConverter {
615 public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType)
617 if (sourceType == typeof (string))
618 return true;
619 return base.CanConvertFrom (context, sourceType);
622 public override object ConvertFrom (ITypeDescriptorContext context,
623 CultureInfo culture, object value)
625 string v = value as string;
626 if (v != null) {
627 switch (v) {
628 case "A": return Foo.A;
629 case "B": return Foo.B;
633 return base.ConvertFrom (context, culture, value);
637 [TypeConverter (typeof(FooConverter))]
638 class Foo {
639 public static readonly Foo A = new Foo ("A");
640 public static readonly Foo B = new Foo ("B");
641 string s;
642 Foo (string s) { this.s = s; }
643 public override string ToString () {return s;}
646 class Test {
647 public static void Main (string[] args)
649 var tests = new Dictionary<string, Action> () {
650 { "boolean", () => CheckBoolean () },
651 { "bundling", () => CheckOptionBundling () },
652 { "context", () => CheckOptionContext () },
653 { "descriptions", () => CheckWriteOptionDescriptions () },
654 { "exceptions", () => CheckExceptions () },
655 { "halt", () => CheckHaltProcessing () },
656 { "localization", () => CheckLocalization () },
657 { "many", () => CheckMany () },
658 { "optional", () => CheckOptional () },
659 { "required", () => CheckRequired () },
660 { "derived-type", () => CheckDerivedType () },
662 bool run = true;
663 bool help = false;
664 var p = new OptionSet () {
665 { "t|test=",
666 "Run the specified test. Valid tests:\n" + new string (' ', 32) +
667 string.Join ("\n" + new string (' ', 32), tests.Keys.OrderBy (s => s).ToArray ()),
668 v => { run = false; Console.WriteLine (v); tests [v] (); } },
669 { "h|?|help", "Show this message and exit", (v) => help = v != null },
671 p.Parse (args);
672 if (help) {
673 Console.WriteLine ("usage: Options.exe [OPTION]+\n");
674 Console.WriteLine ("Options unit test program.");
675 Console.WriteLine ("Valid options include:");
676 p.WriteOptionDescriptions (Console.Out);
677 } else if (run) {
678 foreach (Action a in tests.Values)
679 a ();
683 static IEnumerable<string> _ (params string[] a)
685 return a;
688 static void CheckRequired ()
690 string a = null;
691 int n = 0;
692 OptionSet p = new OptionSet () {
693 { "a=", v => a = v },
694 { "n=", (int v) => n = v },
696 List<string> extra = p.Parse (_("a", "-a", "s", "-n=42", "n"));
697 Assert (extra.Count, 2);
698 Assert (extra [0], "a");
699 Assert (extra [1], "n");
700 Assert (a, "s");
701 Assert (n, 42);
703 extra = p.Parse (_("-a="));
704 Assert (extra.Count, 0);
705 Assert (a, "");
708 static void CheckOptional ()
710 string a = null;
711 int n = -1;
712 Foo f = null;
713 OptionSet p = new OptionSet () {
714 { "a:", v => a = v },
715 { "n:", (int v) => n = v },
716 { "f:", (Foo v) => f = v },
718 p.Parse (_("-a=s"));
719 Assert (a, "s");
720 p.Parse (_("-a"));
721 Assert (a, null);
722 p.Parse (_("-a="));
723 Assert (a, "");
725 p.Parse (_("-f", "A"));
726 Assert (f, Foo.A);
727 p.Parse (_("-f"));
728 Assert (f, null);
730 p.Parse (_("-n", "42"));
731 Assert (n, 42);
732 p.Parse (_("-n"));
733 Assert (n, 0);
736 static void CheckBoolean ()
738 bool a = false;
739 OptionSet p = new OptionSet () {
740 { "a", v => a = v != null },
742 p.Parse (_("-a"));
743 Assert (a, true);
744 p.Parse (_("-a+"));
745 Assert (a, true);
746 p.Parse (_("-a-"));
747 Assert (a, false);
750 static void CheckMany ()
752 int a = -1, b = -1;
753 string av = null, bv = null;
754 Foo f = null;
755 int help = 0;
756 int verbose = 0;
757 OptionSet p = new OptionSet () {
758 { "a=", v => { a = 1; av = v; } },
759 { "b", "desc", v => {b = 2; bv = v;} },
760 { "f=", (Foo v) => f = v },
761 { "v", v => { ++verbose; } },
762 { "h|?|help", (v) => { switch (v) {
763 case "h": help |= 0x1; break;
764 case "?": help |= 0x2; break;
765 case "help": help |= 0x4; break;
766 } } },
768 List<string> e = p.Parse (new string[]{"foo", "-v", "-a=42", "/b-",
769 "-a", "64", "bar", "--f", "B", "/h", "-?", "--help", "-v"});
771 Assert (e.Count, 2);
772 Assert (e[0], "foo");
773 Assert (e[1], "bar");
774 Assert (a, 1);
775 Assert (av, "64");
776 Assert (b, 2);
777 Assert (bv, null);
778 Assert (verbose, 2);
779 Assert (help, 0x7);
780 Assert (f, Foo.B);
783 static void Assert<T>(T actual, T expected)
785 if (!object.Equals (actual, expected))
786 throw new InvalidOperationException (
787 string.Format ("Assertion failed: {0} != {1}", actual, expected));
790 class DefaultOption : Option {
791 public DefaultOption (string prototypes, string description)
792 : base (prototypes, description)
796 protected override void OnParseComplete (OptionContext c)
798 throw new NotImplementedException ();
802 static void CheckExceptions ()
804 string a = null;
805 var p = new OptionSet () {
806 { "a=", v => a = v },
807 { "c", v => { } },
808 { "n=", (int v) => { } },
809 { "f=", (Foo v) => { } },
811 // missing argument
812 AssertException (typeof(OptionException),
813 "Missing required value for option '-a'.",
814 p, v => { v.Parse (_("-a")); });
815 // another named option while expecting one -- follow Getopt::Long
816 AssertException (null, null,
817 p, v => { v.Parse (_("-a", "-a")); });
818 Assert (a, "-a");
819 // no exception when an unregistered named option follows.
820 AssertException (null, null,
821 p, v => { v.Parse (_("-a", "-b")); });
822 Assert (a, "-b");
823 AssertException (typeof(ArgumentNullException),
824 "Argument cannot be null.\nParameter name: option",
825 p, v => { v.Add (null); });
827 // bad type
828 AssertException (typeof(OptionException),
829 "Could not convert string `value' to type Int32 for option `-n'.",
830 p, v => { v.Parse (_("-n", "value")); });
831 AssertException (typeof(OptionException),
832 "Could not convert string `invalid' to type Foo for option `--f'.",
833 p, v => { v.Parse (_("--f", "invalid")); });
835 // try to bundle with an option requiring a value
836 AssertException (typeof(OptionException),
837 "Cannot bundle option '-a' that requires a value.",
838 p, v => { v.Parse (_("-ca", "value")); });
840 AssertException (typeof(ArgumentNullException),
841 "Argument cannot be null.\nParameter name: prototype",
842 p, v => { new DefaultOption (null, null); });
843 AssertException (typeof(ArgumentException),
844 "Cannot be the empty string.\nParameter name: prototype",
845 p, v => { new DefaultOption ("", null); });
846 AssertException (typeof(ArgumentException),
847 "Empty option names are not supported.\nParameter name: prototype",
848 p, v => { new DefaultOption ("a|b||c=", null); });
849 AssertException (typeof(ArgumentException),
850 "Conflicting option types: '=' vs. ':'.\nParameter name: prototype",
851 p, v => { new DefaultOption ("a=|b:", null); });
852 AssertException (typeof(ArgumentNullException),
853 "Argument cannot be null.\nParameter name: action",
854 p, v => { v.Add ("foo", (Action<string>) null); });
855 AssertException (typeof(ArgumentNullException),
856 "Argument cannot be null.\nParameter name: action",
857 p, v => { v.Add ("foo", (Action<string, OptionContext>) null); });
860 static void AssertException<T> (Type exception, string message, T a, Action<T> action)
862 Type actualType = null;
863 string stack = null;
864 string actualMessage = null;
865 try {
866 action (a);
868 catch (Exception e) {
869 actualType = e.GetType ();
870 actualMessage = e.Message;
871 if (!object.Equals (actualType, exception))
872 stack = e.ToString ();
874 if (!object.Equals (actualType, exception)) {
875 throw new InvalidOperationException (
876 string.Format ("Assertion failed: Expected Exception Type {0}, got {1}.\n" +
877 "Actual Exception: {2}", exception, actualType, stack));
879 if (!object.Equals (actualMessage, message))
880 throw new InvalidOperationException (
881 string.Format ("Assertion failed:\n\tExpected: {0}\n\t Actual: {1}",
882 message, actualMessage));
885 static void CheckWriteOptionDescriptions ()
887 var p = new OptionSet () {
888 { "p|indicator-style=", "append / indicator to directories", v => {} },
889 { "color:", "controls color info", v => {} },
890 { "h|?|help", "show help text", v => {} },
891 { "version", "output version information and exit", v => {} },
894 StringWriter expected = new StringWriter ();
895 expected.WriteLine (" -p, --indicator-style=VALUE");
896 expected.WriteLine (" append / indicator to directories");
897 expected.WriteLine (" --color[=VALUE] controls color info");
898 expected.WriteLine (" -h, -?, --help show help text");
899 expected.WriteLine (" --version output version information and exit");
901 StringWriter actual = new StringWriter ();
902 p.WriteOptionDescriptions (actual);
904 Assert (actual.ToString (), expected.ToString ());
907 static void CheckOptionBundling ()
909 string a, b, c;
910 a = b = c = null;
911 var p = new OptionSet () {
912 { "a", v => a = "a" },
913 { "b", v => b = "b" },
914 { "c", v => c = "c" },
916 p.Parse (_ ("-abc"));
917 Assert (a, "a");
918 Assert (b, "b");
919 Assert (c, "c");
922 static void CheckHaltProcessing ()
924 var p = new OptionSet () {
925 { "a", v => {} },
926 { "b", v => {} },
928 List<string> e = p.Parse (_ ("-a", "-b", "--", "-a", "-b"));
929 Assert (e.Count, 2);
930 Assert (e [0], "-a");
931 Assert (e [1], "-b");
934 static void CheckLocalization ()
936 var p = new OptionSet (f => "hello!") {
937 { "n=", (int v) => { } },
939 AssertException (typeof(OptionException), "hello!",
940 p, v => { v.Parse (_("-n=value")); });
942 StringWriter expected = new StringWriter ();
943 expected.WriteLine (" -nhello! hello!");
945 StringWriter actual = new StringWriter ();
946 p.WriteOptionDescriptions (actual);
948 Assert (actual.ToString (), expected.ToString ());
951 class CiOptionSet : OptionSet {
952 protected override void InsertItem (int index, Option item)
954 if (item.Prototype.ToLower () != item.Prototype)
955 throw new ArgumentException ("prototypes must be null!");
956 base.InsertItem (index, item);
959 protected override bool Parse (string option, OptionContext c)
961 if (c.Option != null)
962 return base.Parse (option, c);
963 string f, n, v;
964 if (!GetOptionParts (option, out f, out n, out v)) {
965 return base.Parse (option, c);
967 return base.Parse (f + n.ToLower () + (v != null ? "=" + v : ""), c);
970 public new Option GetOptionForName (string n)
972 return base.GetOptionForName (n);
976 static void CheckDerivedType ()
978 bool help = false;
979 var p = new CiOptionSet () {
980 { "h|help", v => help = v != null },
982 p.Parse (_("-H"));
983 Assert (help, true);
984 help = false;
985 p.Parse (_("-HELP"));
986 Assert (help, true);
988 Assert (p.GetOptionForName ("h"), p [0]);
989 Assert (p.GetOptionForName ("help"), p [0]);
990 Assert (p.GetOptionForName ("invalid"), null);
992 AssertException (typeof(ArgumentException), "prototypes must be null!",
993 p, v => { v.Add ("N|NUM=", (int n) => {}); });
994 AssertException (typeof(ArgumentNullException),
995 "Argument cannot be null.\nParameter name: option",
996 p, v => { v.GetOptionForName (null); });
999 static void CheckOptionContext ()
1001 var p = new OptionSet () {
1002 { "a=", "a desc", (v,c) => {
1003 Assert (v, "a-val");
1004 Assert (c.Option.Description, "a desc");
1005 Assert (c.OptionName, "/a");
1006 Assert (c.OptionIndex, 1);
1007 Assert (c.OptionValue, v);
1008 } },
1009 { "b", "b desc", (v, c) => {
1010 Assert (v, "--b+");
1011 Assert (c.Option.Description, "b desc");
1012 Assert (c.OptionName, "--b+");
1013 Assert (c.OptionIndex, 2);
1014 Assert (c.OptionValue, v);
1015 } },
1016 { "c=", "c desc", (v, c) => {
1017 Assert (v, "C");
1018 Assert (c.Option.Description, "c desc");
1019 Assert (c.OptionName, "--c");
1020 Assert (c.OptionIndex, 3);
1021 Assert (c.OptionValue, v);
1022 } },
1023 { "d", "d desc", (v, c) => {
1024 Assert (v, null);
1025 Assert (c.Option.Description, "d desc");
1026 Assert (c.OptionName, "/d-");
1027 Assert (c.OptionIndex, 4);
1028 Assert (c.OptionValue, v);
1029 } },
1031 p.Parse (_("/a", "a-val", "--b+", "--c=C", "/d-"));
1035 #endif