andrew4582

Command Line Argument Parser

Feb 8th, 2013
365
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 45.06 KB | None | 0 0
  1. //////////////////////////////////////////////////////////////////////////////
  2. //    Command Line Argument Parser
  3. //    ----------------------------
  4. //
  5. //    Author: [email protected]
  6. //
  7. //    Microsoft Public License (Ms-PL)
  8. //
  9. //    This license governs use of the accompanying software. If you use the software, you
  10. //    accept this license. If you do not accept the license, do not use the software.
  11. //
  12. //    1. Definitions
  13. //
  14. //    The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
  15. //    same meaning here as under U.S. copyright law.
  16. //
  17. //    A "contribution" is the original software, or any additions or changes to the software.
  18. //
  19. //    A "contributor" is any person that distributes its contribution under this license.
  20. //
  21. //    "Licensed patents" are a contributor's patent claims that read directly on its contribution.
  22. //
  23. //    2. Grant of Rights
  24. //
  25. //    (A) Copyright Grant- Subject to the terms of this license, including the license conditions
  26. //        and limitations in section 3, each contributor grants you a non-exclusive, worldwide,
  27. //        royalty-free copyright license to reproduce its contribution, prepare derivative works
  28. //        of its contribution, and distribute its contribution or any derivative works that you create.
  29. //
  30. //    (B) Patent Grant- Subject to the terms of this license, including the license conditions and
  31. //        limitations in section 3, each contributor grants you a non-exclusive, worldwide,
  32. //        royalty-free license under its licensed patents to make, have made, use, sell, offer for
  33. //        sale, import, and/or otherwise dispose of its contribution in the software or derivative
  34. //        works of the contribution in the software.
  35. //
  36. //    3. Conditions and Limitations
  37. //
  38. //    (A) No Trademark License- This license does not grant you rights to use any contributors'
  39. //        name, logo, or trademarks.
  40. //
  41. //    (B) If you bring a patent claim against any contributor over patents that you claim are
  42. //        infringed by the software, your patent license from such contributor to the software ends
  43. //        automatically.
  44. //
  45. //    (C) If you distribute any portion of the software, you must retain all copyright, patent,
  46. //        trademark, and attribution notices that are present in the software.
  47. //
  48. //    (D) If you distribute any portion of the software in source code form, you may do so only under
  49. //        this license by including a complete copy of this license with your distribution. If you
  50. //        distribute any portion of the software in compiled or object code form, you may only do so
  51. //        under a license that complies with this license.
  52. //
  53. //    (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no
  54. //        express warranties, guarantees or conditions. You may have additional consumer rights under
  55. //        your local laws which this license cannot change. To the extent permitted under your local
  56. //        laws, the contributors exclude the implied warranties of merchantability, fitness for a
  57. //        particular purpose and non-infringement.
  58. //
  59. //    Usage
  60. //    -----
  61. //
  62. //    Parsing command line arguments to a console application is a common problem.
  63. //    This library handles the common task of reading arguments from a command line
  64. //    and filling in the values in a type.
  65. //
  66. //    To use this library, define a class whose fields represent the data that your
  67. //    application wants to receive from arguments on the command line. Then call
  68. //    CommandLine.ParseArguments() to fill the object with the data
  69. //    from the command line. Each field in the class defines a command line argument.
  70. //    The type of the field is used to validate the data read from the command line.
  71. //    The name of the field defines the name of the command line option.
  72. //
  73. //    The parser can handle fields of the following types:
  74. //
  75. //    - string
  76. //    - int
  77. //    - uint
  78. //    - bool
  79. //    - enum
  80. //    - array of the above type
  81. //
  82. //    For example, suppose you want to read in the argument list for wc (word count).
  83. //    wc takes three optional boolean arguments: -l, -w, and -c and a list of files.
  84. //
  85. //    You could parse these arguments using the following code:
  86. //
  87. //    class WCArguments
  88. //    {
  89. //        public bool lines;
  90. //        public bool words;
  91. //        public bool chars;
  92. //        public string[] files;
  93. //    }
  94. //
  95. //    class WC
  96. //    {
  97. //        static void Main(string[] args)
  98. //        {
  99. //            if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))
  100. //            {
  101. //            //     insert application code here
  102. //            }
  103. //        }
  104. //    }
  105. //
  106. //    So you could call this aplication with the following command line to count
  107. //    lines in the foo and bar files:
  108. //
  109. //        wc.exe /lines /files:foo /files:bar
  110. //
  111. //    The program will display the following usage message when bad command line
  112. //    arguments are used:
  113. //
  114. //        wc.exe -x
  115. //
  116. //    Unrecognized command line argument '-x'
  117. //        /lines[+|-]                         short form /l
  118. //        /words[+|-]                         short form /w
  119. //        /chars[+|-]                         short form /c
  120. //        /files:<string>                     short form /f
  121. //        @<file>                             Read response file for more options
  122. //
  123. //    That was pretty easy. However, you realy want to omit the "/files:" for the
  124. //    list of files. The details of field parsing can be controled using custom
  125. //    attributes. The attributes which control parsing behaviour are:
  126. //
  127. //    ArgumentAttribute
  128. //        - controls short name, long name, required, allow duplicates, default value
  129. //        and help text
  130. //    DefaultArgumentAttribute
  131. //        - allows omition of the "/name".
  132. //        - This attribute is allowed on only one field in the argument class.
  133. //
  134. //    So for the wc.exe program we want this:
  135. //
  136. //    using System;
  137. //    using Utilities;
  138. //
  139. //    class WCArguments
  140. //    {
  141. //        [Argument(ArgumentType.AtMostOnce, HelpText="Count number of lines in the input text.")]
  142. //        public bool lines;
  143. //        [Argument(ArgumentType.AtMostOnce, HelpText="Count number of words in the input text.")]
  144. //        public bool words;
  145. //        [Argument(ArgumentType.AtMostOnce, HelpText="Count number of chars in the input text.")]
  146. //        public bool chars;
  147. //        [DefaultArgument(ArgumentType.MultipleUnique, HelpText="Input files to count.")]
  148. //        public string[] files;
  149. //    }
  150. //
  151. //    class WC
  152. //    {
  153. //        static void Main(string[] args)
  154. //        {
  155. //            WCArguments parsedArgs = new WCArguments();
  156. //            if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))
  157. //            {
  158. //            //     insert application code here
  159. //            }
  160. //        }
  161. //    }
  162. //
  163. //
  164. //
  165. //    So now we have the command line we want:
  166. //
  167. //        wc.exe /lines foo bar
  168. //
  169. //    This will set lines to true and will set files to an array containing the
  170. //    strings "foo" and "bar".
  171. //
  172. //    The new usage message becomes:
  173. //
  174. //        wc.exe -x
  175. //
  176. //    Unrecognized command line argument '-x'
  177. //    /lines[+|-]  Count number of lines in the input text. (short form /l)
  178. //    /words[+|-]  Count number of words in the input text. (short form /w)
  179. //    /chars[+|-]  Count number of chars in the input text. (short form /c)
  180. //    @<file>      Read response file for more options
  181. //    <files>      Input files to count. (short form /f)
  182. //
  183. //    If you want more control over how error messages are reported, how /help is
  184. //    dealt with, etc you can instantiate the CommandLine.Parser class.
  185. //
  186. //
  187. //
  188. //    Cheers,
  189. //    Peter Hallam
  190. //    C# Compiler Developer
  191. //    Microsoft Corp.
  192. //
  193. //
  194. //
  195. //
  196. //    Release Notes
  197. //    -------------
  198. //
  199. //    10/02/2002 Initial Release
  200. //    10/14/2002 Bug Fix
  201. //    01/08/2003 Bug Fix in @ include files
  202. //    10/23/2004 Added user specified help text, formatting of help text to
  203. //            screen width. Added ParseHelp for /?.
  204. //    11/23/2004 Added support for default values.
  205. //    02/23/2005 Fix bug with short name and default arguments.
  206. //////////////////////////////////////////////////////////////////////////////
  207. namespace CommandLine
  208. {
  209.     using System;
  210.     using System.Diagnostics;
  211.     using System.Reflection;
  212.     using System.Collections;
  213.     using System.IO;
  214.     using System.Text;
  215.     using System.Runtime.InteropServices;
  216.  
  217.     /// <summary>
  218.     /// Used to control parsing of command line arguments.
  219.     /// </summary>
  220.     [Flags]
  221.     public enum ArgumentType
  222.     {
  223.         /// <summary>
  224.         /// Indicates that this field is required. An error will be displayed
  225.         /// if it is not present when parsing arguments.
  226.         /// </summary>
  227.         Required = 0x01,
  228.         /// <summary>
  229.         /// Only valid in conjunction with Multiple.
  230.         /// Duplicate values will result in an error.
  231.         /// </summary>
  232.         Unique = 0x02,
  233.         /// <summary>
  234.         /// Inidicates that the argument may be specified more than once.
  235.         /// Only valid if the argument is a collection
  236.         /// </summary>
  237.         Multiple = 0x04,
  238.  
  239.         /// <summary>
  240.         /// The default type for non-collection arguments.
  241.         /// The argument is not required, but an error will be reported if it is specified more than once.
  242.         /// </summary>
  243.         AtMostOnce = 0x00,
  244.  
  245.         /// <summary>
  246.         /// For non-collection arguments, when the argument is specified more than
  247.         /// once no error is reported and the value of the argument is the last
  248.         /// value which occurs in the argument list.
  249.         /// </summary>
  250.         LastOccurenceWins = Multiple,
  251.  
  252.         /// <summary>
  253.         /// The default type for collection arguments.
  254.         /// The argument is permitted to occur multiple times, but duplicate
  255.         /// values will cause an error to be reported.
  256.         /// </summary>
  257.         MultipleUnique = Multiple | Unique,
  258.     }
  259.  
  260.     /// <summary>
  261.     /// Allows control of command line parsing.
  262.     /// Attach this attribute to instance fields of types used
  263.     /// as the destination of command line argument parsing.
  264.     /// </summary>
  265.     [AttributeUsage(AttributeTargets.Field)]
  266.     public class ArgumentAttribute : Attribute
  267.     {
  268.         /// <summary>
  269.         /// Allows control of command line parsing.
  270.         /// </summary>
  271.         /// <param name="type"> Specifies the error checking to be done on the argument. </param>
  272.         public ArgumentAttribute(ArgumentType type)
  273.         {
  274.             this.type = type;
  275.         }
  276.  
  277.         /// <summary>
  278.         /// The error checking to be done on the argument.
  279.         /// </summary>
  280.         public ArgumentType Type
  281.         {
  282.             get { return this.type; }
  283.         }
  284.         /// <summary>
  285.         /// Returns true if the argument did not have an explicit short name specified.
  286.         /// </summary>
  287.         public bool DefaultShortName { get { return null == this.shortName; } }
  288.  
  289.         /// <summary>
  290.         /// The short name of the argument.
  291.         /// Set to null means use the default short name if it does not
  292.         /// conflict with any other parameter name.
  293.         /// Set to String.Empty for no short name.
  294.         /// This property should not be set for DefaultArgumentAttributes.
  295.         /// </summary>
  296.         public string ShortName
  297.         {
  298.             get { return this.shortName; }
  299.             set { Debug.Assert(value == null || !(this is DefaultArgumentAttribute)); this.shortName = value; }
  300.         }
  301.  
  302.         /// <summary>
  303.         /// Returns true if the argument did not have an explicit long name specified.
  304.         /// </summary>
  305.         public bool DefaultLongName { get { return null == this.longName; } }
  306.  
  307.         /// <summary>
  308.         /// The long name of the argument.
  309.         /// Set to null means use the default long name.
  310.         /// The long name for every argument must be unique.
  311.         /// It is an error to specify a long name of String.Empty.
  312.         /// </summary>
  313.         public string LongName
  314.         {
  315.             get { Debug.Assert(!this.DefaultLongName); return this.longName; }
  316.             set { Debug.Assert(value != ""); this.longName = value; }
  317.         }
  318.  
  319.         /// <summary>
  320.         /// The default value of the argument.
  321.         /// </summary>
  322.         public object DefaultValue
  323.         {
  324.             get { return this.defaultValue; }
  325.             set { this.defaultValue = value; }
  326.         }
  327.  
  328.         /// <summary>
  329.         /// Returns true if the argument has a default value.
  330.         /// </summary>
  331.         public bool HasDefaultValue { get { return null != this.defaultValue; } }
  332.  
  333.         /// <summary>
  334.         /// Returns true if the argument has help text specified.
  335.         /// </summary>
  336.         public bool HasHelpText { get { return null != this.helpText; } }
  337.  
  338.         /// <summary>
  339.         /// The help text for the argument.
  340.         /// </summary>
  341.         public string HelpText
  342.         {
  343.             get { return this.helpText; }
  344.             set { this.helpText = value; }
  345.         }
  346.  
  347.         private string shortName;
  348.         private string longName;
  349.         private string helpText;
  350.         private object defaultValue;
  351.         private ArgumentType type;
  352.     }
  353.  
  354.     /// <summary>
  355.     /// Indicates that this argument is the default argument.
  356.     /// '/' or '-' prefix only the argument value is specified.
  357.     /// The ShortName property should not be set for DefaultArgumentAttribute
  358.     /// instances. The LongName property is used for usage text only and
  359.     /// does not affect the usage of the argument.
  360.     /// </summary>
  361.     [AttributeUsage(AttributeTargets.Field)]
  362.     public class DefaultArgumentAttribute : ArgumentAttribute
  363.     {
  364.         /// <summary>
  365.         /// Indicates that this argument is the default argument.
  366.         /// </summary>
  367.         /// <param name="type"> Specifies the error checking to be done on the argument. </param>
  368.         public DefaultArgumentAttribute(ArgumentType type)
  369.             : base(type)
  370.         {
  371.         }
  372.     }
  373.  
  374.     /// <summary>
  375.     /// A delegate used in error reporting.
  376.     /// </summary>
  377.     public delegate void ErrorReporter(string message);
  378.  
  379.     /// <summary>
  380.     /// Parser for command line arguments.
  381.     ///
  382.     /// The parser specification is infered from the instance fields of the object
  383.     /// specified as the destination of the parse.
  384.     /// Valid argument types are: int, uint, string, bool, enums
  385.     /// Also argument types of Array of the above types are also valid.
  386.     ///
  387.     /// Error checking options can be controlled by adding a ArgumentAttribute
  388.     /// to the instance fields of the destination object.
  389.     ///
  390.     /// At most one field may be marked with the DefaultArgumentAttribute
  391.     /// indicating that arguments without a '-' or '/' prefix will be parsed as that argument.
  392.     ///
  393.     /// If not specified then the parser will infer default options for parsing each
  394.     /// instance field. The default long name of the argument is the field name. The
  395.     /// default short name is the first character of the long name. Long names and explicitly
  396.     /// specified short names must be unique. Default short names will be used provided that
  397.     /// the default short name does not conflict with a long name or an explicitly
  398.     /// specified short name.
  399.     ///
  400.     /// Arguments which are array types are collection arguments. Collection
  401.     /// arguments can be specified multiple times.
  402.     /// </summary>
  403.     public sealed class Parser
  404.     {
  405.         /// <summary>
  406.         /// The System Defined new line string.
  407.         /// </summary>
  408.         public const string NewLine = "\r\n";
  409.  
  410.         /// <summary>
  411.         /// Don't ever call this.
  412.         /// </summary>
  413.         private Parser() { }
  414.  
  415.         /// <summary>
  416.         /// Parses Command Line Arguments. Displays usage message to Console.Out
  417.         /// if /?, /help or invalid arguments are encounterd.
  418.         /// Errors are output on Console.Error.
  419.         /// Use ArgumentAttributes to control parsing behaviour.
  420.         /// </summary>
  421.         /// <param name="arguments"> The actual arguments. </param>
  422.         /// <param name="destination"> The resulting parsed arguments. </param>
  423.         /// <returns> true if no errors were detected. </returns>
  424.         public static bool ParseArgumentsWithUsage(string[] arguments, object destination)
  425.         {
  426.             if (Parser.ParseHelp(arguments) || !Parser.ParseArguments(arguments, destination))
  427.             {
  428.                 // error encountered in arguments. Display usage message
  429.                 System.Console.Write(Parser.ArgumentsUsage(destination.GetType()));
  430.                 return false;
  431.             }
  432.  
  433.             return true;
  434.         }
  435.  
  436.         /// <summary>
  437.         /// Parses Command Line Arguments.
  438.         /// Errors are output on Console.Error.
  439.         /// Use ArgumentAttributes to control parsing behaviour.
  440.         /// </summary>
  441.         /// <param name="arguments"> The actual arguments. </param>
  442.         /// <param name="destination"> The resulting parsed arguments. </param>
  443.         /// <returns> true if no errors were detected. </returns>
  444.         public static bool ParseArguments(string[] arguments, object destination)
  445.         {
  446.             return Parser.ParseArguments(arguments, destination, new ErrorReporter(Console.Error.WriteLine));
  447.         }
  448.  
  449.         /// <summary>
  450.         /// Parses Command Line Arguments.
  451.         /// Use ArgumentAttributes to control parsing behaviour.
  452.         /// </summary>
  453.         /// <param name="arguments"> The actual arguments. </param>
  454.         /// <param name="destination"> The resulting parsed arguments. </param>
  455.         /// <param name="reporter"> The destination for parse errors. </param>
  456.         /// <returns> true if no errors were detected. </returns>
  457.         public static bool ParseArguments(string[] arguments, object destination, ErrorReporter reporter)
  458.         {
  459.             Parser parser = new Parser(destination.GetType(), reporter);
  460.             return parser.Parse(arguments, destination);
  461.         }
  462.  
  463.         private static void NullErrorReporter(string message)
  464.         {
  465.         }
  466.  
  467.         private class HelpArgument
  468.         {
  469.             [ArgumentAttribute(ArgumentType.AtMostOnce, ShortName = "?")]
  470.             public bool help = false;
  471.         }
  472.  
  473.         /// <summary>
  474.         /// Checks if a set of arguments asks for help.
  475.         /// </summary>
  476.         /// <param name="args"> Args to check for help. </param>
  477.         /// <returns> Returns true if args contains /? or /help. </returns>
  478.         public static bool ParseHelp(string[] args)
  479.         {
  480.             Parser helpParser = new Parser(typeof(HelpArgument), new ErrorReporter(NullErrorReporter));
  481.             HelpArgument helpArgument = new HelpArgument();
  482.             helpParser.Parse(args, helpArgument);
  483.             return helpArgument.help;
  484.         }
  485.  
  486.         /// <summary>
  487.         /// Returns a Usage string for command line argument parsing.
  488.         /// Use ArgumentAttributes to control parsing behaviour.
  489.         /// Formats the output to the width of the current console window.
  490.         /// </summary>
  491.         /// <param name="T"> The type of the arguments to display usage for. </param>
  492.         /// <returns> Printable string containing a user friendly description of command line arguments. </returns>
  493.         public static string ArgumentsUsage<T>()
  494.         {
  495.             Type argumentType = typeof(T);
  496.             return ArgumentsUsage(argumentType);
  497.         }
  498.  
  499.         /// <summary>
  500.         /// Returns a Usage string for command line argument parsing.
  501.         /// Use ArgumentAttributes to control parsing behaviour.
  502.         /// </summary>
  503.         /// <param name="T"> The type of the arguments to display usage for. </param>
  504.         /// <param name="columns"> The number of columns to format the output to. </param>
  505.         /// <returns> Printable string containing a user friendly description of command line arguments. </returns>
  506.         public static string ArgumentsUsage<T>(int columns)
  507.         {
  508.             Type argumentType = typeof(T);
  509.             return ArgumentsUsage(argumentType, columns);
  510.         }
  511.  
  512.         /// <summary>
  513.         /// Returns a Usage string for command line argument parsing.
  514.         /// Use ArgumentAttributes to control parsing behaviour.
  515.         /// Formats the output to the width of the current console window.
  516.         /// </summary>
  517.         /// <param name="argumentType"> The type of the arguments to display usage for. </param>
  518.         /// <returns> Printable string containing a user friendly description of command line arguments. </returns>
  519.         public static string ArgumentsUsage(Type argumentType)
  520.         {
  521.             int screenWidth = Parser.GetConsoleWindowWidth();
  522.             if (screenWidth == 0)
  523.                 screenWidth = 80;
  524.             return ArgumentsUsage(argumentType, screenWidth);
  525.         }
  526.  
  527.         /// <summary>
  528.         /// Returns a Usage string for command line argument parsing.
  529.         /// Use ArgumentAttributes to control parsing behaviour.
  530.         /// </summary>
  531.         /// <param name="argumentType"> The type of the arguments to display usage for. </param>
  532.         /// <param name="columns"> The number of columns to format the output to. </param>
  533.         /// <returns> Printable string containing a user friendly description of command line arguments. </returns>
  534.         public static string ArgumentsUsage(Type argumentType, int columns)
  535.         {
  536.             return (new Parser(argumentType, null)).GetUsageString(columns);
  537.         }
  538.  
  539.  
  540.  
  541.         private const int STD_OUTPUT_HANDLE = -11;
  542.  
  543.         private struct COORD
  544.         {
  545.             internal Int16 x;
  546.             internal Int16 y;
  547.         }
  548.  
  549.         private struct SMALL_RECT
  550.         {
  551.             internal Int16 Left;
  552.             internal Int16 Top;
  553.             internal Int16 Right;
  554.             internal Int16 Bottom;
  555.         }
  556.  
  557.         private struct CONSOLE_SCREEN_BUFFER_INFO
  558.         {
  559.             internal COORD dwSize;
  560.             internal COORD dwCursorPosition;
  561.             internal Int16 wAttributes;
  562.             internal SMALL_RECT srWindow;
  563.             internal COORD dwMaximumWindowSize;
  564.         }
  565.  
  566.         [DllImport("kernel32.dll", EntryPoint = "GetStdHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  567.         private static extern int GetStdHandle(int nStdHandle);
  568.  
  569.         [DllImport("kernel32.dll", EntryPoint = "GetConsoleScreenBufferInfo", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  570.         private static extern int GetConsoleScreenBufferInfo(int hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
  571.  
  572.         /// <summary>
  573.         /// Returns the number of columns in the current console window
  574.         /// </summary>
  575.         /// <returns>Returns the number of columns in the current console window</returns>
  576.         public static int GetConsoleWindowWidth()
  577.         {
  578.             int screenWidth;
  579.             CONSOLE_SCREEN_BUFFER_INFO csbi = new CONSOLE_SCREEN_BUFFER_INFO();
  580.  
  581.             int rc;
  582.             rc = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref csbi);
  583.             screenWidth = csbi.dwSize.x;
  584.             return screenWidth;
  585.         }
  586.  
  587.         /// <summary>
  588.         /// Searches a StringBuilder for a character
  589.         /// </summary>
  590.         /// <param name="text"> The text to search. </param>
  591.         /// <param name="value"> The character value to search for. </param>
  592.         /// <param name="startIndex"> The index to stat searching at. </param>
  593.         /// <returns> The index of the first occurence of value or -1 if it is not found. </returns>
  594.         public static int IndexOf(StringBuilder text, char value, int startIndex)
  595.         {
  596.             for (int index = startIndex; index < text.Length; index++)
  597.             {
  598.                 if (text[index] == value)
  599.                     return index;
  600.             }
  601.  
  602.             return -1;
  603.         }
  604.  
  605.         /// <summary>
  606.         /// Searches a StringBuilder for a character in reverse
  607.         /// </summary>
  608.         /// <param name="text"> The text to search. </param>
  609.         /// <param name="value"> The character to search for. </param>
  610.         /// <param name="startIndex"> The index to start the search at. </param>
  611.         /// <returns>The index of the last occurence of value in text or -1 if it is not found. </returns>
  612.         public static int LastIndexOf(StringBuilder text, char value, int startIndex)
  613.         {
  614.             for (int index = Math.Min(startIndex, text.Length - 1); index >= 0; index--)
  615.             {
  616.                 if (text[index] == value)
  617.                     return index;
  618.             }
  619.  
  620.             return -1;
  621.         }
  622.  
  623.         private const int spaceBeforeParam = 2;
  624.  
  625.         /// <summary>
  626.         /// Creates a new command line argument parser.
  627.         /// </summary>
  628.         /// <param name="argumentSpecification"> The type of object to  parse. </param>
  629.         /// <param name="reporter"> The destination for parse errors. </param>
  630.         public Parser(Type argumentSpecification, ErrorReporter reporter)
  631.         {
  632.             this.reporter = reporter;
  633.             this.arguments = new ArrayList();
  634.             this.argumentMap = new Hashtable();
  635.  
  636.             foreach (FieldInfo field in argumentSpecification.GetFields())
  637.             {
  638.                 if (!field.IsStatic && !field.IsInitOnly && !field.IsLiteral)
  639.                 {
  640.                     ArgumentAttribute attribute = GetAttribute(field);
  641.                     if (attribute is DefaultArgumentAttribute)
  642.                     {
  643.                         Debug.Assert(this.defaultArgument == null);
  644.                         this.defaultArgument = new Argument(attribute, field, reporter);
  645.                     }
  646.                     else
  647.                     {
  648.                         this.arguments.Add(new Argument(attribute, field, reporter));
  649.                     }
  650.                 }
  651.             }
  652.  
  653.             // add explicit names to map
  654.             foreach (Argument argument in this.arguments)
  655.             {
  656.                 Debug.Assert(!argumentMap.ContainsKey(argument.LongName));
  657.                 this.argumentMap[argument.LongName] = argument;
  658.                 if (argument.ExplicitShortName)
  659.                 {
  660.                     if (argument.ShortName != null && argument.ShortName.Length > 0)
  661.                     {
  662.                         Debug.Assert(!argumentMap.ContainsKey(argument.ShortName));
  663.                         this.argumentMap[argument.ShortName] = argument;
  664.                     }
  665.                     else
  666.                     {
  667.                         argument.ClearShortName();
  668.                     }
  669.                 }
  670.             }
  671.  
  672.             // add implicit names which don't collide to map
  673.             foreach (Argument argument in this.arguments)
  674.             {
  675.                 if (!argument.ExplicitShortName)
  676.                 {
  677.                     if (argument.ShortName != null && argument.ShortName.Length > 0 && !argumentMap.ContainsKey(argument.ShortName))
  678.                         this.argumentMap[argument.ShortName] = argument;
  679.                     else
  680.                         argument.ClearShortName();
  681.                 }
  682.             }
  683.         }
  684.  
  685.         private static ArgumentAttribute GetAttribute(FieldInfo field)
  686.         {
  687.             object[] attributes = field.GetCustomAttributes(typeof(ArgumentAttribute), false);
  688.             if (attributes.Length == 1)
  689.                 return (ArgumentAttribute)attributes[0];
  690.  
  691.             Debug.Assert(attributes.Length == 0);
  692.             return null;
  693.         }
  694.  
  695.         private void ReportUnrecognizedArgument(string argument)
  696.         {
  697.             this.reporter(string.Format("Unrecognized command line argument '{0}'", argument));
  698.         }
  699.  
  700.         /// <summary>
  701.         /// Parses an argument list into an object
  702.         /// </summary>
  703.         /// <param name="args"></param>
  704.         /// <param name="destination"></param>
  705.         /// <returns> true if an error occurred </returns>
  706.         private bool ParseArgumentList(string[] args, object destination)
  707.         {
  708.             bool hadError = false;
  709.             if (args != null)
  710.             {
  711.                 foreach (string argument in args)
  712.                 {
  713.                     if (argument.Length > 0)
  714.                     {
  715.                         switch (argument[0])
  716.                         {
  717.                             case '-':
  718.                             case '/':
  719.                                 int endIndex = argument.IndexOfAny(new char[] { ':', '+', '-' }, 1);
  720.                                 string option = argument.Substring(1, endIndex == -1 ? argument.Length - 1 : endIndex - 1);
  721.                                 string optionArgument;
  722.                                 if (option.Length + 1 == argument.Length)
  723.                                 {
  724.                                     optionArgument = null;
  725.                                 }
  726.                                 else if (argument.Length > 1 + option.Length && argument[1 + option.Length] == ':')
  727.                                 {
  728.                                     optionArgument = argument.Substring(option.Length + 2);
  729.                                 }
  730.                                 else
  731.                                 {
  732.                                     optionArgument = argument.Substring(option.Length + 1);
  733.                                 }
  734.  
  735.                                 Argument arg = (Argument)this.argumentMap[option];
  736.                                 if (arg == null)
  737.                                 {
  738.                                     ReportUnrecognizedArgument(argument);
  739.                                     hadError = true;
  740.                                 }
  741.                                 else
  742.                                 {
  743.                                     hadError |= !arg.SetValue(optionArgument, destination);
  744.                                 }
  745.                                 break;
  746.                             case '@':
  747.                                 string[] nestedArguments;
  748.                                 hadError |= LexFileArguments(argument.Substring(1), out nestedArguments);
  749.                                 hadError |= ParseArgumentList(nestedArguments, destination);
  750.                                 break;
  751.                             default:
  752.                                 if (this.defaultArgument != null)
  753.                                 {
  754.                                     hadError |= !this.defaultArgument.SetValue(argument, destination);
  755.                                 }
  756.                                 else
  757.                                 {
  758.                                     ReportUnrecognizedArgument(argument);
  759.                                     hadError = true;
  760.                                 }
  761.                                 break;
  762.                         }
  763.                     }
  764.                 }
  765.             }
  766.  
  767.             return hadError;
  768.         }
  769.  
  770.         /// <summary>
  771.         /// Parses an argument list.
  772.         /// </summary>
  773.         /// <param name="args"> The arguments to parse. </param>
  774.         /// <param name="destination"> The destination of the parsed arguments. </param>
  775.         /// <returns> true if no parse errors were encountered. </returns>
  776.         public bool Parse(string[] args, object destination)
  777.         {
  778.             bool hadError = ParseArgumentList(args, destination);
  779.  
  780.             // check for missing required arguments
  781.             foreach (Argument arg in this.arguments)
  782.             {
  783.                 hadError |= arg.Finish(destination);
  784.             }
  785.             if (this.defaultArgument != null)
  786.             {
  787.                 hadError |= this.defaultArgument.Finish(destination);
  788.             }
  789.  
  790.             return !hadError;
  791.         }
  792.  
  793.         private struct ArgumentHelpStrings
  794.         {
  795.             public ArgumentHelpStrings(string syntax, string help)
  796.             {
  797.                 this.syntax = syntax;
  798.                 this.help = help;
  799.             }
  800.  
  801.             public string syntax;
  802.             public string help;
  803.         }
  804.  
  805.         /// <summary>
  806.         /// A user firendly usage string describing the command line argument syntax.
  807.         /// </summary>
  808.         public string GetUsageString(int screenWidth)
  809.         {
  810.             ArgumentHelpStrings[] strings = GetAllHelpStrings();
  811.  
  812.             int maxParamLen = 0;
  813.             foreach (ArgumentHelpStrings helpString in strings)
  814.             {
  815.                 maxParamLen = Math.Max(maxParamLen, helpString.syntax.Length);
  816.             }
  817.  
  818.             const int minimumNumberOfCharsForHelpText = 10;
  819.             const int minimumHelpTextColumn = 5;
  820.             const int minimumScreenWidth = minimumHelpTextColumn + minimumNumberOfCharsForHelpText;
  821.  
  822.             int helpTextColumn;
  823.             int idealMinimumHelpTextColumn = maxParamLen + spaceBeforeParam;
  824.             screenWidth = Math.Max(screenWidth, minimumScreenWidth);
  825.             if (screenWidth < (idealMinimumHelpTextColumn + minimumNumberOfCharsForHelpText))
  826.                 helpTextColumn = minimumHelpTextColumn;
  827.             else
  828.                 helpTextColumn = idealMinimumHelpTextColumn;
  829.  
  830.             const string newLine = "\n";
  831.             StringBuilder builder = new StringBuilder();
  832.             foreach (ArgumentHelpStrings helpStrings in strings)
  833.             {
  834.                 // add syntax string
  835.                 int syntaxLength = helpStrings.syntax.Length;
  836.                 builder.Append(helpStrings.syntax);
  837.  
  838.                 // start help text on new line if syntax string is too long
  839.                 int currentColumn = syntaxLength;
  840.                 if (syntaxLength >= helpTextColumn)
  841.                 {
  842.                     builder.Append(newLine);
  843.                     currentColumn = 0;
  844.                 }
  845.  
  846.                 // add help text broken on spaces
  847.                 int charsPerLine = screenWidth - helpTextColumn;
  848.                 int index = 0;
  849.                 while (index < helpStrings.help.Length)
  850.                 {
  851.                     // tab to start column
  852.                     builder.Append(' ', helpTextColumn - currentColumn);
  853.                     currentColumn = helpTextColumn;
  854.  
  855.                     // find number of chars to display on this line
  856.                     int endIndex = index + charsPerLine;
  857.                     if (endIndex >= helpStrings.help.Length)
  858.                     {
  859.                         // rest of text fits on this line
  860.                         endIndex = helpStrings.help.Length;
  861.                     }
  862.                     else
  863.                     {
  864.                         endIndex = helpStrings.help.LastIndexOf(' ', endIndex - 1, Math.Min(endIndex - index, charsPerLine));
  865.                         if (endIndex <= index)
  866.                         {
  867.                             // no spaces on this line, append full set of chars
  868.                             endIndex = index + charsPerLine;
  869.                         }
  870.                     }
  871.  
  872.                     // add chars
  873.                     builder.Append(helpStrings.help, index, endIndex - index);
  874.                     index = endIndex;
  875.  
  876.                     // do new line
  877.                     AddNewLine(newLine, builder, ref currentColumn);
  878.  
  879.                     // don't start a new line with spaces
  880.                     while (index < helpStrings.help.Length && helpStrings.help[index] == ' ')
  881.                         index++;
  882.                 }
  883.  
  884.                 // add newline if there's no help text                
  885.                 if (helpStrings.help.Length == 0)
  886.                 {
  887.                     builder.Append(newLine);
  888.                 }
  889.             }
  890.  
  891.             return builder.ToString();
  892.         }
  893.         private static void AddNewLine(string newLine, StringBuilder builder, ref int currentColumn)
  894.         {
  895.             builder.Append(newLine);
  896.             currentColumn = 0;
  897.         }
  898.         private ArgumentHelpStrings[] GetAllHelpStrings()
  899.         {
  900.             ArgumentHelpStrings[] strings = new ArgumentHelpStrings[NumberOfParametersToDisplay()];
  901.  
  902.             int index = 0;
  903.             foreach (Argument arg in this.arguments)
  904.             {
  905.                 strings[index] = GetHelpStrings(arg);
  906.                 index++;
  907.             }
  908.  
  909.             strings[index++] = new ArgumentHelpStrings("", "");
  910.             if (this.defaultArgument != null)
  911.                 strings[index++] = GetHelpStrings(this.defaultArgument);
  912.  
  913.             return strings;
  914.         }
  915.  
  916.         private static ArgumentHelpStrings GetHelpStrings(Argument arg)
  917.         {
  918.             return new ArgumentHelpStrings(arg.SyntaxHelp, arg.FullHelpText);
  919.         }
  920.  
  921.         private int NumberOfParametersToDisplay()
  922.         {
  923.             int numberOfParameters = this.arguments.Count + 1;
  924.             if (HasDefaultArgument)
  925.                 numberOfParameters += 1;
  926.             return numberOfParameters;
  927.         }
  928.  
  929.         /// <summary>
  930.         /// Does this parser have a default argument.
  931.         /// </summary>
  932.         /// <value> Does this parser have a default argument. </value>
  933.         public bool HasDefaultArgument
  934.         {
  935.             get { return this.defaultArgument != null; }
  936.         }
  937.  
  938.         private bool LexFileArguments(string fileName, out string[] arguments)
  939.         {
  940.             string args = null;
  941.  
  942.             try
  943.             {
  944.                 using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read))
  945.                 {
  946.                     args = (new StreamReader(file)).ReadToEnd();
  947.                 }
  948.             }
  949.             catch (Exception e)
  950.             {
  951.                 this.reporter(string.Format("Error: Can't open command line argument file '{0}' : '{1}'", fileName, e.Message));
  952.                 arguments = null;
  953.                 return false;
  954.             }
  955.  
  956.             bool hadError = false;
  957.             ArrayList argArray = new ArrayList();
  958.             StringBuilder currentArg = new StringBuilder();
  959.             bool inQuotes = false;
  960.             int index = 0;
  961.  
  962.             // while (index < args.Length)
  963.             try
  964.             {
  965.                 while (true)
  966.                 {
  967.                     // skip whitespace
  968.                     while (char.IsWhiteSpace(args[index]))
  969.                     {
  970.                         index += 1;
  971.                     }
  972.  
  973.                     // # - comment to end of line
  974.                     if (args[index] == '#')
  975.                     {
  976.                         index += 1;
  977.                         while (args[index] != '\n')
  978.                         {
  979.                             index += 1;
  980.                         }
  981.                         continue;
  982.                     }
  983.  
  984.                     // do one argument
  985.                     do
  986.                     {
  987.                         if (args[index] == '\\')
  988.                         {
  989.                             int cSlashes = 1;
  990.                             index += 1;
  991.                             while (index == args.Length && args[index] == '\\')
  992.                             {
  993.                                 cSlashes += 1;
  994.                             }
  995.  
  996.                             if (index == args.Length || args[index] != '"')
  997.                             {
  998.                                 currentArg.Append('\\', cSlashes);
  999.                             }
  1000.                             else
  1001.                             {
  1002.                                 currentArg.Append('\\', (cSlashes >> 1));
  1003.                                 if (0 != (cSlashes & 1))
  1004.                                 {
  1005.                                     currentArg.Append('"');
  1006.                                 }
  1007.                                 else
  1008.                                 {
  1009.                                     inQuotes = !inQuotes;
  1010.                                 }
  1011.                             }
  1012.                         }
  1013.                         else if (args[index] == '"')
  1014.                         {
  1015.                             inQuotes = !inQuotes;
  1016.                             index += 1;
  1017.                         }
  1018.                         else
  1019.                         {
  1020.                             currentArg.Append(args[index]);
  1021.                             index += 1;
  1022.                         }
  1023.                     } while (!char.IsWhiteSpace(args[index]) || inQuotes);
  1024.                     argArray.Add(currentArg.ToString());
  1025.                     currentArg.Length = 0;
  1026.                 }
  1027.             }
  1028.             catch (System.IndexOutOfRangeException)
  1029.             {
  1030.                 // got EOF
  1031.                 if (inQuotes)
  1032.                 {
  1033.                     this.reporter(string.Format("Error: Unbalanced '\"' in command line argument file '{0}'", fileName));
  1034.                     hadError = true;
  1035.                 }
  1036.                 else if (currentArg.Length > 0)
  1037.                 {
  1038.                     // valid argument can be terminated by EOF
  1039.                     argArray.Add(currentArg.ToString());
  1040.                 }
  1041.             }
  1042.  
  1043.             arguments = (string[])argArray.ToArray(typeof(string));
  1044.             return hadError;
  1045.         }
  1046.  
  1047.         private static string LongName(ArgumentAttribute attribute, FieldInfo field)
  1048.         {
  1049.             return (attribute == null || attribute.DefaultLongName) ? field.Name : attribute.LongName;
  1050.         }
  1051.  
  1052.         private static string ShortName(ArgumentAttribute attribute, FieldInfo field)
  1053.         {
  1054.             if (attribute is DefaultArgumentAttribute)
  1055.                 return null;
  1056.             if (!ExplicitShortName(attribute))
  1057.                 return LongName(attribute, field).Substring(0, 1);
  1058.             return attribute.ShortName;
  1059.         }
  1060.  
  1061.         private static string HelpText(ArgumentAttribute attribute, FieldInfo field)
  1062.         {
  1063.             if (attribute == null)
  1064.                 return null;
  1065.             else
  1066.                 return attribute.HelpText;
  1067.         }
  1068.  
  1069.         private static bool HasHelpText(ArgumentAttribute attribute)
  1070.         {
  1071.             return (attribute != null && attribute.HasHelpText);
  1072.         }
  1073.  
  1074.         private static bool ExplicitShortName(ArgumentAttribute attribute)
  1075.         {
  1076.             return (attribute != null && !attribute.DefaultShortName);
  1077.         }
  1078.  
  1079.         private static object DefaultValue(ArgumentAttribute attribute, FieldInfo field)
  1080.         {
  1081.             return (attribute == null || !attribute.HasDefaultValue) ? null : attribute.DefaultValue;
  1082.         }
  1083.  
  1084.         private static Type ElementType(FieldInfo field)
  1085.         {
  1086.             if (IsCollectionType(field.FieldType))
  1087.                 return field.FieldType.GetElementType();
  1088.             else
  1089.                 return null;
  1090.         }
  1091.  
  1092.         private static ArgumentType Flags(ArgumentAttribute attribute, FieldInfo field)
  1093.         {
  1094.             if (attribute != null)
  1095.                 return attribute.Type;
  1096.             else if (IsCollectionType(field.FieldType))
  1097.                 return ArgumentType.MultipleUnique;
  1098.             else
  1099.                 return ArgumentType.AtMostOnce;
  1100.         }
  1101.  
  1102.         private static bool IsCollectionType(Type type)
  1103.         {
  1104.             return type.IsArray;
  1105.         }
  1106.  
  1107.         private static bool IsValidElementType(Type type)
  1108.         {
  1109.             return type != null && (
  1110.                 type == typeof(int) ||
  1111.                 type == typeof(uint) ||
  1112.                 type == typeof(string) ||
  1113.                 type == typeof(bool) ||
  1114.                 type.IsEnum);
  1115.         }
  1116.  
  1117.         [System.Diagnostics.DebuggerDisplay("Name = {LongName}")]
  1118.         private class Argument
  1119.         {
  1120.             public Argument(ArgumentAttribute attribute, FieldInfo field, ErrorReporter reporter)
  1121.             {
  1122.                 this.longName = Parser.LongName(attribute, field);
  1123.                 this.explicitShortName = Parser.ExplicitShortName(attribute);
  1124.                 this.shortName = Parser.ShortName(attribute, field);
  1125.                 this.hasHelpText = Parser.HasHelpText(attribute);
  1126.                 this.helpText = Parser.HelpText(attribute, field);
  1127.                 this.defaultValue = Parser.DefaultValue(attribute, field);
  1128.                 this.elementType = ElementType(field);
  1129.                 this.flags = Flags(attribute, field);
  1130.                 this.field = field;
  1131.                 this.seenValue = false;
  1132.                 this.reporter = reporter;
  1133.                 this.isDefault = attribute != null && attribute is DefaultArgumentAttribute;
  1134.  
  1135.                 if (IsCollection)
  1136.                 {
  1137.                     this.collectionValues = new ArrayList();
  1138.                 }
  1139.  
  1140.                 Debug.Assert(this.longName != null && this.longName != "");
  1141.                 Debug.Assert(!this.isDefault || !this.ExplicitShortName);
  1142.                 Debug.Assert(!IsCollection || AllowMultiple, "Collection arguments must have allow multiple");
  1143.                 Debug.Assert(!Unique || IsCollection, "Unique only applicable to collection arguments");
  1144.                 Debug.Assert(IsValidElementType(Type) ||
  1145.                     IsCollectionType(Type));
  1146.                 Debug.Assert((IsCollection && IsValidElementType(elementType)) ||
  1147.                     (!IsCollection && elementType == null));
  1148.                 Debug.Assert(!(this.IsRequired && this.HasDefaultValue), "Required arguments cannot have default value");
  1149.                 Debug.Assert(!this.HasDefaultValue || (this.defaultValue.GetType() == field.FieldType), "Type of default value must match field type");
  1150.             }
  1151.  
  1152.             public bool Finish(object destination)
  1153.             {
  1154.                 if (this.SeenValue)
  1155.                 {
  1156.                     if (this.IsCollection)
  1157.                     {
  1158.                         this.field.SetValue(destination, this.collectionValues.ToArray(this.elementType));
  1159.                     }
  1160.                 }
  1161.                 else
  1162.                 {
  1163.                     if (this.HasDefaultValue)
  1164.                     {
  1165.                         this.field.SetValue(destination, this.DefaultValue);
  1166.                     }
  1167.                 }
  1168.  
  1169.                 return ReportMissingRequiredArgument();
  1170.             }
  1171.  
  1172.             private bool ReportMissingRequiredArgument()
  1173.             {
  1174.                 if (this.IsRequired && !this.SeenValue)
  1175.                 {
  1176.                     if (this.IsDefault)
  1177.                         reporter(string.Format("Missing required argument '<{0}>'.", this.LongName));
  1178.                     else
  1179.                         reporter(string.Format("Missing required argument '/{0}'.", this.LongName));
  1180.                     return true;
  1181.                 }
  1182.                 return false;
  1183.             }
  1184.  
  1185.             private void ReportDuplicateArgumentValue(string value)
  1186.             {
  1187.                 this.reporter(string.Format("Duplicate '{0}' argument '{1}'", this.LongName, value));
  1188.             }
  1189.  
  1190.             public bool SetValue(string value, object destination)
  1191.             {
  1192.                 if (SeenValue && !AllowMultiple)
  1193.                 {
  1194.                     this.reporter(string.Format("Duplicate '{0}' argument", this.LongName));
  1195.                     return false;
  1196.                 }
  1197.                 this.seenValue = true;
  1198.  
  1199.                 object newValue;
  1200.                 if (!ParseValue(this.ValueType, value, out newValue))
  1201.                     return false;
  1202.                 if (this.IsCollection)
  1203.                 {
  1204.                     if (this.Unique && this.collectionValues.Contains(newValue))
  1205.                     {
  1206.                         ReportDuplicateArgumentValue(value);
  1207.                         return false;
  1208.                     }
  1209.                     else
  1210.                     {
  1211.                         this.collectionValues.Add(newValue);
  1212.                     }
  1213.                 }
  1214.                 else
  1215.                 {
  1216.                     this.field.SetValue(destination, newValue);
  1217.                 }
  1218.  
  1219.                 return true;
  1220.             }
  1221.  
  1222.             public Type ValueType
  1223.             {
  1224.                 get { return this.IsCollection ? this.elementType : this.Type; }
  1225.             }
  1226.  
  1227.             private void ReportBadArgumentValue(string value)
  1228.             {
  1229.                 this.reporter(string.Format("'{0}' is not a valid value for the '{1}' command line option", value, this.LongName));
  1230.             }
  1231.  
  1232.             private bool ParseValue(Type type, string stringData, out object value)
  1233.             {
  1234.                 // null is only valid for bool variables
  1235.                 // empty string is never valid
  1236.                 if ((stringData != null || type == typeof(bool)) && (stringData == null || stringData.Length > 0))
  1237.                 {
  1238.                     try
  1239.                     {
  1240.                         if (type == typeof(string))
  1241.                         {
  1242.                             value = stringData;
  1243.                             return true;
  1244.                         }
  1245.                         else if (type == typeof(bool))
  1246.                         {
  1247.                             if (stringData == null || stringData == "+")
  1248.                             {
  1249.                                 value = true;
  1250.                                 return true;
  1251.                             }
  1252.                             else if (stringData == "-")
  1253.                             {
  1254.                                 value = false;
  1255.                                 return true;
  1256.                             }
  1257.                         }
  1258.                         else if (type == typeof(int))
  1259.                         {
  1260.                             value = int.Parse(stringData);
  1261.                             return true;
  1262.                         }
  1263.                         else if (type == typeof(uint))
  1264.                         {
  1265.                             value = int.Parse(stringData);
  1266.                             return true;
  1267.                         }
  1268.                         else
  1269.                         {
  1270.                             Debug.Assert(type.IsEnum);
  1271.  
  1272.                             bool valid = false;
  1273.                             foreach (string name in Enum.GetNames(type))
  1274.                             {
  1275.                                 if (name == stringData)
  1276.                                 {
  1277.                                     valid = true;
  1278.                                     break;
  1279.                                 }
  1280.                             }
  1281.                             if (valid)
  1282.                             {
  1283.                                 value = Enum.Parse(type, stringData, true);
  1284.                                 return true;
  1285.                             }
  1286.                         }
  1287.                     }
  1288.                     catch
  1289.                     {
  1290.                         // catch parse errors
  1291.                     }
  1292.                 }
  1293.  
  1294.                 ReportBadArgumentValue(stringData);
  1295.                 value = null;
  1296.                 return false;
  1297.             }
  1298.  
  1299.             private void AppendValue(StringBuilder builder, object value)
  1300.             {
  1301.                 if (value is string || value is int || value is uint || value.GetType().IsEnum)
  1302.                 {
  1303.                     builder.Append(value.ToString());
  1304.                 }
  1305.                 else if (value is bool)
  1306.                 {
  1307.                     builder.Append((bool)value ? "+" : "-");
  1308.                 }
  1309.                 else
  1310.                 {
  1311.                     bool first = true;
  1312.                     foreach (object o in (System.Array)value)
  1313.                     {
  1314.                         if (!first)
  1315.                         {
  1316.                             builder.Append(", ");
  1317.                         }
  1318.                         AppendValue(builder, o);
  1319.                         first = false;
  1320.                     }
  1321.                 }
  1322.             }
  1323.  
  1324.             public string LongName
  1325.             {
  1326.                 get { return this.longName; }
  1327.             }
  1328.  
  1329.             public bool ExplicitShortName
  1330.             {
  1331.                 get { return this.explicitShortName; }
  1332.             }
  1333.  
  1334.             public string ShortName
  1335.             {
  1336.                 get { return this.shortName; }
  1337.             }
  1338.  
  1339.             public bool HasShortName
  1340.             {
  1341.                 get { return this.shortName != null; }
  1342.             }
  1343.  
  1344.             public void ClearShortName()
  1345.             {
  1346.                 this.shortName = null;
  1347.             }
  1348.  
  1349.             public bool HasHelpText
  1350.             {
  1351.                 get { return this.hasHelpText; }
  1352.             }
  1353.  
  1354.             public string HelpText
  1355.             {
  1356.                 get { return this.helpText; }
  1357.             }
  1358.  
  1359.             public object DefaultValue
  1360.             {
  1361.                 get { return this.defaultValue; }
  1362.             }
  1363.  
  1364.             public bool HasDefaultValue
  1365.             {
  1366.                 get { return null != this.defaultValue; }
  1367.             }
  1368.  
  1369.             public string FullHelpText
  1370.             {
  1371.                 get
  1372.                 {
  1373.                     StringBuilder builder = new StringBuilder();
  1374.                     if (this.HasHelpText)
  1375.                     {
  1376.                         builder.Append(this.HelpText);
  1377.                     }
  1378.                     if (this.HasDefaultValue)
  1379.                     {
  1380.                         if (builder.Length > 0)
  1381.                             builder.Append(" ");
  1382.                         builder.Append("Default value:'");
  1383.                         AppendValue(builder, this.DefaultValue);
  1384.                         builder.Append('\'');
  1385.                     }
  1386.                     if (this.HasShortName)
  1387.                     {
  1388.                         if (builder.Length > 0)
  1389.                             builder.Append(" ");
  1390.                         builder.Append("(short form /");
  1391.                         builder.Append(this.ShortName);
  1392.                         builder.Append(")");
  1393.                     }
  1394.                     return builder.ToString();
  1395.                 }
  1396.             }
  1397.  
  1398.             public string SyntaxHelp
  1399.             {
  1400.                 get
  1401.                 {
  1402.                     StringBuilder builder = new StringBuilder();
  1403.  
  1404.                     if (this.IsDefault)
  1405.                     {
  1406.                         builder.Append("<");
  1407.                         builder.Append(this.LongName);
  1408.                         builder.Append(">");
  1409.                     }
  1410.                     else
  1411.                     {
  1412.                         builder.Append("/");
  1413.                         builder.Append(this.LongName);
  1414.                         Type valueType = this.ValueType;
  1415.                         if (valueType == typeof(int))
  1416.                         {
  1417.                             builder.Append(":<int>");
  1418.                         }
  1419.                         else if (valueType == typeof(uint))
  1420.                         {
  1421.                             builder.Append(":<uint>");
  1422.                         }
  1423.                         else if (valueType == typeof(bool))
  1424.                         {
  1425.                             builder.Append("[+|-]");
  1426.                         }
  1427.                         else if (valueType == typeof(string))
  1428.                         {
  1429.                             builder.Append(":<string>");
  1430.                         }
  1431.                         else
  1432.                         {
  1433.                             Debug.Assert(valueType.IsEnum);
  1434.  
  1435.                             builder.Append(":{");
  1436.                             bool first = true;
  1437.                             foreach (FieldInfo field in valueType.GetFields())
  1438.                             {
  1439.                                 if (field.IsStatic)
  1440.                                 {
  1441.                                     if (first)
  1442.                                         first = false;
  1443.                                     else
  1444.                                         builder.Append('|');
  1445.                                     builder.Append(field.Name);
  1446.                                 }
  1447.                             }
  1448.                             builder.Append('}');
  1449.                         }
  1450.                     }
  1451.  
  1452.                     return builder.ToString();
  1453.                 }
  1454.             }
  1455.  
  1456.             public bool IsRequired
  1457.             {
  1458.                 get { return 0 != (this.flags & ArgumentType.Required); }
  1459.             }
  1460.  
  1461.             public bool SeenValue
  1462.             {
  1463.                 get { return this.seenValue; }
  1464.             }
  1465.  
  1466.             public bool AllowMultiple
  1467.             {
  1468.                 get { return 0 != (this.flags & ArgumentType.Multiple); }
  1469.             }
  1470.  
  1471.             public bool Unique
  1472.             {
  1473.                 get { return 0 != (this.flags & ArgumentType.Unique); }
  1474.             }
  1475.  
  1476.             public Type Type
  1477.             {
  1478.                 get { return field.FieldType; }
  1479.             }
  1480.  
  1481.             public bool IsCollection
  1482.             {
  1483.                 get { return IsCollectionType(Type); }
  1484.             }
  1485.  
  1486.             public bool IsDefault
  1487.             {
  1488.                 get { return this.isDefault; }
  1489.             }
  1490.  
  1491.             private string longName;
  1492.             private string shortName;
  1493.             private string helpText;
  1494.             private bool hasHelpText;
  1495.             private bool explicitShortName;
  1496.             private object defaultValue;
  1497.             private bool seenValue;
  1498.             private FieldInfo field;
  1499.             private Type elementType;
  1500.             private ArgumentType flags;
  1501.             private ArrayList collectionValues;
  1502.             private ErrorReporter reporter;
  1503.             private bool isDefault;
  1504.         }
  1505.  
  1506.         private ArrayList arguments;
  1507.         private Hashtable argumentMap;
  1508.         private Argument defaultArgument;
  1509.         private ErrorReporter reporter;
  1510.     }
  1511. }
Advertisement
Add Comment
Please, Sign In to add comment