**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.GetOptions / Mono.GetOptions / OptionDetails.cs
blob9b347d240f4fe21505ae64b0c0a70651df746d53
1 //
2 // OptionDetails.cs
3 //
4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
5 //
6 // (C) 2002 Rafael Teixeira
7 //
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System;
31 using System.Collections;
32 using System.IO;
33 using System.Reflection;
35 namespace Mono.GetOptions
37 public enum WhatToDoNext
39 AbandonProgram,
40 GoAhead
43 internal enum OptionProcessingResult
45 NotThisOption,
46 OptionAlone,
47 OptionConsumedParameter
50 internal class OptionDetails : IComparable
52 public string ShortForm;
53 public string LongForm;
54 public string AlternateForm;
55 public string ShortDescription;
56 public bool NeedsParameter;
57 public int MaxOccurs; // negative means there is no limit
58 public int Occurs;
59 public bool BooleanOption;
60 public Options OptionBundle;
61 public MemberInfo MemberInfo;
62 public ArrayList Values;
63 public System.Type ParameterType;
64 public string paramName = null;
66 private string ExtractParamName(string shortDescription)
68 int whereBegins = shortDescription.IndexOf("{");
69 if (whereBegins < 0)
70 paramName = "PARAM";
71 else {
72 int whereEnds = shortDescription.IndexOf("}");
73 if (whereEnds < whereBegins)
74 whereEnds = shortDescription.Length+1;
76 paramName = shortDescription.Substring(whereBegins + 1, whereEnds - whereBegins - 1);
77 shortDescription =
78 shortDescription.Substring(0, whereBegins) +
79 paramName +
80 shortDescription.Substring(whereEnds + 1);
82 return shortDescription;
85 public string ParamName
87 get
89 return paramName;
93 public static bool Verbose = false;
95 private OptionsParsingMode parsingMode { get { return this.OptionBundle.ParsingMode; } }
97 private string linuxLongPrefix {
98 get {
99 return (((parsingMode & OptionsParsingMode.GNU_DoubleDash) == OptionsParsingMode.GNU_DoubleDash)? "--":"-");
103 public string DefaultForm
105 get {
106 string shortPrefix = "-";
107 string longPrefix = linuxLongPrefix;
108 if (parsingMode == OptionsParsingMode.Windows) {
109 shortPrefix = "/";
110 longPrefix = "/";
112 if (this.ShortForm != string.Empty)
113 return shortPrefix+this.ShortForm;
114 else
115 return longPrefix+this.LongForm;
119 public override string ToString()
121 string optionHelp;
122 // TODO: Yet not that good
123 string shortPrefix;
124 string longPrefix;
125 bool hasLongForm = (this.LongForm != null && this.LongForm != string.Empty);
126 if(this.OptionBundle.ParsingMode == OptionsParsingMode.Windows)
128 shortPrefix = "/";
129 longPrefix = "/";
131 else
133 shortPrefix = "-";
134 longPrefix = linuxLongPrefix;
136 optionHelp = " ";
137 optionHelp += (this.ShortForm != string.Empty) ? shortPrefix+this.ShortForm+" " : " ";
138 optionHelp += hasLongForm ? longPrefix+this.LongForm : "";
139 if (NeedsParameter)
141 if (hasLongForm)
142 optionHelp += ":";
143 optionHelp += ParamName;
145 optionHelp = optionHelp.PadRight(32) + " ";
146 optionHelp += this.ShortDescription;
147 if (this.AlternateForm != string.Empty && this.AlternateForm != null)
148 optionHelp += " [/"+this.AlternateForm + "]";
149 return optionHelp;
152 private static System.Type TypeOfMember(MemberInfo memberInfo)
154 if ((memberInfo.MemberType == MemberTypes.Field && memberInfo is FieldInfo))
155 return ((FieldInfo)memberInfo).FieldType;
157 if ((memberInfo.MemberType == MemberTypes.Property && memberInfo is PropertyInfo))
158 return ((PropertyInfo)memberInfo).PropertyType;
160 if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo))
162 if (((MethodInfo)memberInfo).ReturnType.FullName != typeof(WhatToDoNext).FullName)
163 throw new NotSupportedException("Option method must return '" + typeof(WhatToDoNext).FullName + "'");
165 ParameterInfo[] parameters = ((MethodInfo)memberInfo).GetParameters();
166 if ((parameters == null) || (parameters.Length == 0))
167 return null;
168 else
169 return parameters[0].ParameterType;
172 throw new NotSupportedException("'" + memberInfo.MemberType + "' memberType is not supported");
175 public OptionDetails(MemberInfo memberInfo, OptionAttribute option, Options optionBundle)
177 this.ShortForm = ("" + option.ShortForm).Trim();
178 if (option.LongForm == null)
179 this.LongForm = string.Empty;
180 else
181 this.LongForm = (option.LongForm == string.Empty)? memberInfo.Name:option.LongForm;
182 this.AlternateForm = option.AlternateForm;
183 this.ShortDescription = ExtractParamName(option.ShortDescription);
184 this.Occurs = 0;
185 this.OptionBundle = optionBundle;
186 this.BooleanOption = false;
187 this.MemberInfo = memberInfo;
188 this.NeedsParameter = false;
189 this.Values = null;
190 this.MaxOccurs = 1;
191 this.ParameterType = TypeOfMember(memberInfo);
193 if (this.ParameterType != null)
195 if (this.ParameterType.FullName != "System.Boolean")
197 if (this.LongForm.IndexOf(':') >= 0)
198 throw new InvalidOperationException("Options with an embedded colon (':') in their visible name must be boolean!!! [" +
199 this.MemberInfo.ToString() + " isn't]");
201 this.NeedsParameter = true;
203 if (option.MaxOccurs != 1)
205 if (this.ParameterType.IsArray)
207 this.Values = new ArrayList();
208 this.MaxOccurs = option.MaxOccurs;
210 else
212 if (this.MemberInfo is MethodInfo || this.MemberInfo is PropertyInfo)
213 this.MaxOccurs = option.MaxOccurs;
214 else
215 throw new InvalidOperationException("MaxOccurs set to non default value (" + option.MaxOccurs + ") for a [" +
216 this.MemberInfo.ToString() + "] option");
220 else
222 this.BooleanOption = true;
223 if (option.MaxOccurs != 1)
225 if (this.MemberInfo is MethodInfo || this.MemberInfo is PropertyInfo)
226 this.MaxOccurs = option.MaxOccurs;
227 else
228 throw new InvalidOperationException("MaxOccurs set to non default value (" + option.MaxOccurs + ") for a [" +
229 this.MemberInfo.ToString() + "] option");
235 internal string Key
237 get { return this.ShortForm + this.LongForm; }
240 int IComparable.CompareTo(object other)
242 return Key.CompareTo(((OptionDetails)other).Key);
245 public void TransferValues()
247 if (Values != null)
249 if (MemberInfo is FieldInfo)
251 ((FieldInfo)MemberInfo).SetValue(OptionBundle, Values.ToArray(ParameterType.GetElementType()));
252 return;
255 if (MemberInfo is PropertyInfo)
257 ((PropertyInfo)MemberInfo).SetValue(OptionBundle, Values.ToArray(ParameterType.GetElementType()), null);
258 return;
261 if ((WhatToDoNext)((MethodInfo)MemberInfo).Invoke(OptionBundle, new object[] { Values.ToArray(ParameterType.GetElementType()) }) == WhatToDoNext.AbandonProgram)
262 System.Environment.Exit(1);
266 private void Occurred(int howMany)
268 Occurs += howMany;
270 if (MaxOccurs > 0 && Occurs > MaxOccurs)
271 throw new IndexOutOfRangeException("Option " + ShortForm + " can be used at most " + MaxOccurs + " times");
274 private void DoIt(bool setValue)
276 if (!NeedsParameter)
278 Occurred(1);
280 if (Verbose)
281 Console.WriteLine("<" + this.LongForm + "> set to [true]");
283 if (MemberInfo is FieldInfo)
285 ((FieldInfo)MemberInfo).SetValue(OptionBundle, setValue);
286 return;
288 if (MemberInfo is PropertyInfo)
290 ((PropertyInfo)MemberInfo).SetValue(OptionBundle, setValue, null);
291 return;
293 if ((WhatToDoNext)((MethodInfo)MemberInfo).Invoke(OptionBundle, null) == WhatToDoNext.AbandonProgram)
294 System.Environment.Exit(1);
296 return;
300 private void DoIt(string parameterValue)
302 if (parameterValue == null)
303 parameterValue = "";
305 string[] parameterValues = parameterValue.Split(',');
307 Occurred(parameterValues.Length);
309 foreach (string parameter in parameterValues)
312 object convertedParameter = null;
314 if (Verbose)
315 Console.WriteLine("<" + this.LongForm + "> set to [" + parameter + "]");
317 if (Values != null && parameter != null) {
318 try {
319 convertedParameter = Convert.ChangeType(parameter, ParameterType.GetElementType());
320 } catch (Exception ex) {
321 Console.WriteLine(String.Format("The value '{0}' is not convertible to the appropriate type '{1}' for the {2} option", parameter, ParameterType.GetElementType().Name, DefaultForm));
323 Values.Add(convertedParameter);
324 continue;
327 if (parameter != null) {
328 try {
329 convertedParameter = Convert.ChangeType(parameter, ParameterType);
330 } catch (Exception ex) {
331 Console.WriteLine(String.Format("The value '{0}' is not convertible to the appropriate type '{1}' for the {2} option", parameter, ParameterType.Name, DefaultForm));
332 continue;
336 if (MemberInfo is FieldInfo) {
337 ((FieldInfo)MemberInfo).SetValue(OptionBundle, convertedParameter);
338 continue;
341 if (MemberInfo is PropertyInfo) {
342 ((PropertyInfo)MemberInfo).SetValue(OptionBundle, convertedParameter, null);
343 continue;
346 if ((WhatToDoNext)((MethodInfo)MemberInfo).Invoke(OptionBundle, new object[] { convertedParameter }) == WhatToDoNext.AbandonProgram)
347 System.Environment.Exit(1);
351 private bool IsThisOption(string arg)
353 if (arg != null && arg != string.Empty)
355 arg = arg.TrimStart('-', '/');
356 return (arg == ShortForm || arg == LongForm || arg == AlternateForm);
358 return false;
361 public OptionProcessingResult ProcessArgument(string arg, string nextArg)
363 if (IsThisOption(arg))
365 if (!NeedsParameter)
367 DoIt(true); // in preparation for vbc-like booleans
368 return OptionProcessingResult.OptionAlone;
370 else
372 DoIt(nextArg);
373 return OptionProcessingResult.OptionConsumedParameter;
377 if (IsThisOption(arg + ":" + nextArg))
379 DoIt(true);
380 return OptionProcessingResult.OptionConsumedParameter;
383 return OptionProcessingResult.NotThisOption;