Fix all CreateInstance overloads for void
[mcs.git] / nunit24 / ClientUtilities / util / CommandLineOptions.cs
blob004ccee7af69482ef866318f26cb150784aa91cc
1 // File: CommandLineOptions.cs
2 //
3 // This is a re-usable component to be used when you
4 // need to parse command-line options/parameters.
5 //
6 // Separates command line parameters from command line options.
7 // Uses reflection to populate member variables the derived class with the values
8 // of the options.
9 //
10 // An option can start with "-" or "--". On Windows systems, it can start with "/" as well.
12 // I define 3 types of "options":
13 // 1. Boolean options (yes/no values), e.g: /r to recurse
14 // 2. Value options, e.g: /loglevel=3
15 // 2. Parameters: standalone strings like file names
17 // An example to explain:
18 // csc /nologo /t:exe myfile.cs
19 // | | |
20 // | | + parameter
21 // | |
22 // | + value option
23 // |
24 // + boolean option
26 // Please see a short description of the CommandLineOptions class
27 // at http://codeblast.com/~gert/dotnet/sells.html
28 //
29 // Gert Lombard (gert@codeblast.com)
30 // James Newkirk (jim@nunit.org)
32 namespace Codeblast
34 using System;
35 using System.Reflection;
36 using System.Collections;
37 using System.Text;
40 // The Attributes
43 [AttributeUsage(AttributeTargets.Field)]
44 public class OptionAttribute : Attribute
46 protected object optValue;
47 protected string optName;
48 protected string description;
50 public string Short
52 get { return optName; }
53 set { optName = value; }
56 public object Value
58 get { return optValue; }
59 set { optValue = value; }
62 public string Description
64 get { return description; }
65 set { description = value; }
70 // The CommandLineOptions members
73 public abstract class CommandLineOptions
75 protected ArrayList parameters;
76 protected bool isInvalid = false;
78 private int optionCount;
79 private ArrayList invalidArguments = new ArrayList();
80 private bool allowForwardSlash;
82 public CommandLineOptions( string[] args )
83 : this( System.IO.Path.DirectorySeparatorChar != '/', args ) {}
85 public CommandLineOptions( bool allowForwardSlash, string[] args )
87 this.allowForwardSlash = allowForwardSlash;
88 optionCount = Init( args );
91 public IList InvalidArguments
93 get { return invalidArguments; }
96 public bool NoArgs
98 get
100 return ParameterCount == 0 && optionCount == 0;
104 public bool AllowForwardSlash
106 get { return allowForwardSlash; }
109 public int Init(params string[] args)
111 int count = 0;
112 int n = 0;
113 while (n < args.Length)
115 int pos = IsOption(args[n]);
116 if (pos > 0)
118 // It's an option:
119 if (GetOption(args, ref n, pos))
120 count++;
121 else
122 InvalidOption(args[Math.Min(n, args.Length-1)]);
124 else
126 if (parameters == null) parameters = new ArrayList();
127 parameters.Add(args[n]);
128 if ( !IsValidParameter(args[n]) )
129 InvalidOption( args[n] );
131 n++;
133 return count;
136 // An option starts with "/", "-" or "--":
137 protected virtual int IsOption(string opt)
139 char[] c = null;
140 if (opt.Length < 2)
142 return 0;
144 else if (opt.Length > 2)
146 c = opt.ToCharArray(0, 3);
147 if (c[0] == '-' && c[1] == '-' && IsOptionNameChar(c[2])) return 2;
149 else
151 c = opt.ToCharArray(0, 2);
153 if ((c[0] == '-' || c[0] == '/' && AllowForwardSlash) && IsOptionNameChar(c[1])) return 1;
154 return 0;
157 protected virtual bool IsOptionNameChar(char c)
159 return Char.IsLetterOrDigit(c) || c == '?';
162 protected virtual void InvalidOption(string name)
164 invalidArguments.Add( name );
165 isInvalid = true;
168 protected virtual bool IsValidParameter(string param)
170 return true;
173 protected virtual bool MatchShortName(FieldInfo field, string name)
175 object[] atts = field.GetCustomAttributes(typeof(OptionAttribute), true);
176 foreach (OptionAttribute att in atts)
178 if (string.Compare(att.Short, name, true) == 0) return true;
180 return false;
183 protected virtual FieldInfo GetMemberField(string name)
185 Type t = this.GetType();
186 FieldInfo[] fields = t.GetFields(BindingFlags.Instance|BindingFlags.Public);
187 foreach (FieldInfo field in fields)
189 if (string.Compare(field.Name, name, true) == 0) return field;
190 if (MatchShortName(field, name)) return field;
192 return null;
195 protected virtual object GetOptionValue(FieldInfo field)
197 object[] atts = field.GetCustomAttributes(typeof(OptionAttribute), true);
198 if (atts.Length > 0)
200 OptionAttribute att = (OptionAttribute)atts[0];
201 return att.Value;
203 return null;
206 protected virtual bool GetOption(string[] args, ref int index, int pos)
210 object cmdLineVal = null;
211 string opt = args[index].Substring(pos, args[index].Length-pos);
212 SplitOptionAndValue(ref opt, ref cmdLineVal);
213 FieldInfo field = GetMemberField(opt);
214 if (field != null)
216 object value = GetOptionValue(field);
217 if (value == null)
219 if (field.FieldType == typeof(bool))
220 value = true; // default for bool values is true
221 else if(field.FieldType == typeof(string))
223 value = cmdLineVal != null ? cmdLineVal : args[++index];
224 field.SetValue(this, Convert.ChangeType(value, field.FieldType));
225 string stringValue = (string)value;
226 if(stringValue == null || stringValue.Length == 0) return false;
227 return true;
229 else if(field.FieldType.IsEnum)
230 value = Enum.Parse( field.FieldType, (string)cmdLineVal, true );
231 else
232 value = cmdLineVal != null ? cmdLineVal : args[++index];
234 field.SetValue(this, Convert.ChangeType(value, field.FieldType));
235 return true;
238 catch (Exception)
240 // Ignore exceptions like type conversion errors.
242 return false;
245 protected virtual void SplitOptionAndValue(ref string opt, ref object val)
247 // Look for ":" or "=" separator in the option:
248 int pos = opt.IndexOfAny( new char[] { ':', '=' } );
249 if (pos < 1) return;
251 val = opt.Substring(pos+1);
252 opt = opt.Substring(0, pos);
255 // Parameter accessor:
256 public string this[int index]
260 if (parameters != null) return (string)parameters[index];
261 return null;
265 public ArrayList Parameters
267 get { return parameters; }
270 public int ParameterCount
274 return parameters == null ? 0 : parameters.Count;
278 public virtual void Help()
280 Console.WriteLine(GetHelpText());
283 public virtual string GetHelpText()
285 StringBuilder helpText = new StringBuilder();
287 Type t = this.GetType();
288 FieldInfo[] fields = t.GetFields(BindingFlags.Instance|BindingFlags.Public);
289 char optChar = allowForwardSlash ? '/' : '-';
290 foreach (FieldInfo field in fields)
292 object[] atts = field.GetCustomAttributes(typeof(OptionAttribute), true);
293 if (atts.Length > 0)
295 OptionAttribute att = (OptionAttribute)atts[0];
296 if (att.Description != null)
298 string valType = "";
299 if (att.Value == null)
301 if (field.FieldType == typeof(float)) valType = "=FLOAT";
302 else if (field.FieldType == typeof(string)) valType = "=STR";
303 else if (field.FieldType != typeof(bool)) valType = "=X";
306 helpText.AppendFormat("{0}{1,-20}\t{2}", optChar, field.Name+valType, att.Description);
307 if (att.Short != null)
308 helpText.AppendFormat(" (Short format: {0}{1}{2})", optChar, att.Short, valType);
309 helpText.Append( Environment.NewLine );
313 return helpText.ToString();