Fixing an issue with output parameters that are of type IntPtr
[castle.git] / MonoRail / NewGenerator / Castle.NewGenerator.Core / GetOptions / OptionDetails.cs
blob517a89d0b6e247e2019af0f318482de7522fa652
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 namespace Mono.GetOptions
32 using System;
33 using System.Collections;
34 using System.Reflection;
36 public enum WhatToDoNext
38 AbandonProgram,
39 GoAhead
42 internal enum OptionProcessingResult
44 NotThisOption,
45 OptionAlone,
46 OptionConsumedParameter
49 internal class OptionDetails : IComparable
51 public string ShortForm;
52 public string LongForm;
53 public string AlternateForm;
54 public string ShortDescription;
55 public bool NeedsParameter;
56 public int MaxOccurs; // negative means there is no limit
57 public int Occurs;
58 public bool BooleanOption;
59 public Options OptionBundle;
60 public MemberInfo MemberInfo;
61 public ArrayList Values;
62 public Type ParameterType;
63 public string paramName = null;
64 public bool VBCStyleBoolean;
65 public bool SecondLevelHelp;
66 public bool Hidden;
68 public OptionDetails NextAlternate = null;
70 private string ExtractParamName(string shortDescription)
72 int whereBegins = shortDescription.IndexOf("{");
73 if (whereBegins < 0)
74 paramName = "PARAM";
75 else
77 int whereEnds = shortDescription.IndexOf("}");
78 if (whereEnds < whereBegins)
79 whereEnds = shortDescription.Length + 1;
81 paramName = shortDescription.Substring(whereBegins + 1, whereEnds - whereBegins - 1);
82 shortDescription =
83 shortDescription.Substring(0, whereBegins) +
84 paramName +
85 shortDescription.Substring(whereEnds + 1);
87 return shortDescription;
90 public string ParamName
92 get { return paramName; }
95 private bool verboseParsing
97 get { return OptionBundle.VerboseParsingOfOptions || OptionBundle.DebuggingOfOptions; }
100 // private bool debugOptions { get { return OptionBundle.DebuggingOfOptions; } }
102 private OptionsParsingMode parsingMode
104 get { return OptionBundle.ParsingMode; }
107 private bool useGNUFormat
109 get { return (parsingMode & OptionsParsingMode.GNU_DoubleDash) == OptionsParsingMode.GNU_DoubleDash; }
112 private bool dontSplitOnCommas
114 get { return OptionBundle.DontSplitOnCommas; }
117 private string linuxLongPrefix
119 get { return (useGNUFormat ? "--" : "-"); }
122 public string DefaultForm
126 string shortPrefix = "-";
127 string longPrefix = linuxLongPrefix;
128 if (parsingMode == OptionsParsingMode.Windows)
130 shortPrefix = "/";
131 longPrefix = "/";
133 if (ShortForm != string.Empty)
134 return shortPrefix + ShortForm;
135 else
136 return longPrefix + LongForm;
140 private string optionHelp = null;
142 public override string ToString()
144 if (optionHelp == null)
146 string shortPrefix;
147 string longPrefix;
148 bool hasLongForm = (LongForm != null && LongForm != string.Empty);
149 if (OptionBundle.ParsingMode == OptionsParsingMode.Windows)
151 shortPrefix = "/";
152 longPrefix = "/";
154 else
156 shortPrefix = "-";
157 longPrefix = linuxLongPrefix;
159 optionHelp = " ";
160 optionHelp += (ShortForm != string.Empty) ? shortPrefix + ShortForm + " " : " ";
161 optionHelp += hasLongForm ? longPrefix + LongForm : "";
162 if (NeedsParameter)
164 if (hasLongForm)
165 optionHelp += ":";
166 optionHelp += ParamName;
168 else if (BooleanOption && VBCStyleBoolean)
170 optionHelp += "[+|-]";
172 optionHelp += "\t";
173 if (AlternateForm != string.Empty && AlternateForm != null)
174 optionHelp += "Also " + shortPrefix + AlternateForm + (NeedsParameter ? (":" + ParamName) : "") + ". ";
175 optionHelp += ShortDescription;
177 return optionHelp;
180 private static Type TypeOfMember(MemberInfo memberInfo)
182 if ((memberInfo.MemberType == MemberTypes.Field && memberInfo is FieldInfo))
183 return ((FieldInfo) memberInfo).FieldType;
185 if ((memberInfo.MemberType == MemberTypes.Property && memberInfo is PropertyInfo))
186 return ((PropertyInfo) memberInfo).PropertyType;
188 if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo))
190 if (((MethodInfo) memberInfo).ReturnType.FullName != typeof(WhatToDoNext).FullName)
191 throw new NotSupportedException("Option method must return '" + typeof(WhatToDoNext).FullName + "'");
193 ParameterInfo[] parameters = ((MethodInfo) memberInfo).GetParameters();
194 if ((parameters == null) || (parameters.Length == 0))
195 return null;
196 else
197 return parameters[0].ParameterType;
200 throw new NotSupportedException("'" + memberInfo.MemberType + "' memberType is not supported");
203 public OptionDetails(MemberInfo memberInfo, OptionAttribute option, Options optionBundle)
205 ShortForm = ("" + option.ShortForm).Trim();
206 if (option.LongForm == null)
207 LongForm = string.Empty;
208 else
209 LongForm = (option.LongForm == string.Empty) ? memberInfo.Name : option.LongForm;
210 AlternateForm = option.AlternateForm;
211 ShortDescription = ExtractParamName(option.ShortDescription);
212 Occurs = 0;
213 OptionBundle = optionBundle;
214 BooleanOption = false;
215 MemberInfo = memberInfo;
216 NeedsParameter = false;
217 Values = null;
218 MaxOccurs = 1;
219 VBCStyleBoolean = option.VBCStyleBoolean;
220 SecondLevelHelp = option.SecondLevelHelp;
221 Hidden = false; // TODO: check other attributes
223 ParameterType = TypeOfMember(memberInfo);
225 if (ParameterType != null)
227 if (ParameterType.FullName != "System.Boolean")
229 if (LongForm.IndexOf(':') >= 0)
230 throw new InvalidOperationException(
231 "Options with an embedded colon (':') in their visible name must be boolean!!! [" +
232 MemberInfo.ToString() + " isn't]");
234 NeedsParameter = true;
236 if (option.MaxOccurs != 1)
238 if (ParameterType.IsArray)
240 Values = new ArrayList();
241 MaxOccurs = option.MaxOccurs;
243 else
245 if (MemberInfo is MethodInfo || MemberInfo is PropertyInfo)
246 MaxOccurs = option.MaxOccurs;
247 else
248 throw new InvalidOperationException("MaxOccurs set to non default value (" + option.MaxOccurs + ") for a [" +
249 MemberInfo.ToString() + "] option");
253 else
255 BooleanOption = true;
256 if (option.MaxOccurs != 1)
258 if (MemberInfo is MethodInfo || MemberInfo is PropertyInfo)
259 MaxOccurs = option.MaxOccurs;
260 else
261 throw new InvalidOperationException("MaxOccurs set to non default value (" + option.MaxOccurs + ") for a [" +
262 MemberInfo.ToString() + "] option");
268 internal string Key
272 if (useGNUFormat)
274 string ShortID = ShortForm.ToUpper();
275 if (ShortID == string.Empty)
276 ShortID = "ZZ";
277 return ShortID + " " + LongForm;
279 else
280 return LongForm + " " + ShortForm;
284 int IComparable.CompareTo(object other)
286 return Key.CompareTo(((OptionDetails) other).Key);
289 public void TransferValues()
291 if (Values != null)
293 if (MemberInfo is FieldInfo)
295 ((FieldInfo) MemberInfo).SetValue(OptionBundle, Values.ToArray(ParameterType.GetElementType()));
296 return;
299 if (MemberInfo is PropertyInfo)
301 ((PropertyInfo) MemberInfo).SetValue(OptionBundle, Values.ToArray(ParameterType.GetElementType()), null);
302 return;
305 if (
306 (WhatToDoNext)
307 ((MethodInfo) MemberInfo).Invoke(OptionBundle, new object[] {Values.ToArray(ParameterType.GetElementType())}) ==
308 WhatToDoNext.AbandonProgram)
309 Environment.Exit(1);
313 private int HowManyBeforeExceedingMaxOccurs(int howMany)
315 if (MaxOccurs > 0 && (Occurs + howMany) > MaxOccurs)
317 Console.Error.WriteLine("Option " + LongForm + " can be used at most " + MaxOccurs + " times. Ignoring extras...");
318 howMany = MaxOccurs - Occurs;
320 Occurs += howMany;
321 return howMany;
324 private bool AddingOneMoreExceedsMaxOccurs
326 get { return HowManyBeforeExceedingMaxOccurs(1) < 1; }
329 private void DoIt(bool setValue)
331 if (AddingOneMoreExceedsMaxOccurs)
332 return;
334 if (verboseParsing)
335 Console.WriteLine("<{0}> set to [{1}]", LongForm, setValue);
337 if (MemberInfo is FieldInfo)
339 ((FieldInfo) MemberInfo).SetValue(OptionBundle, setValue);
340 return;
342 if (MemberInfo is PropertyInfo)
344 ((PropertyInfo) MemberInfo).SetValue(OptionBundle, setValue, null);
345 return;
347 if ((WhatToDoNext) ((MethodInfo) MemberInfo).Invoke(OptionBundle, null) == WhatToDoNext.AbandonProgram)
348 Environment.Exit(1);
351 private void DoIt(string parameterValue)
353 if (parameterValue == null)
354 parameterValue = "";
356 string[] parameterValues;
358 if (dontSplitOnCommas || MaxOccurs == 1)
359 parameterValues = new string[] {parameterValue};
360 else
361 parameterValues = parameterValue.Split(',');
363 int waitingToBeProcessed = HowManyBeforeExceedingMaxOccurs(parameterValues.Length);
365 foreach(string parameter in parameterValues)
367 if (waitingToBeProcessed-- <= 0)
368 break;
370 object convertedParameter = null;
372 if (verboseParsing)
373 Console.WriteLine("<" + LongForm + "> set to [" + parameter + "]");
375 if (Values != null && parameter != null)
379 convertedParameter = Convert.ChangeType(parameter, ParameterType.GetElementType());
381 catch(Exception)
383 Console.WriteLine(
384 String.Format("The value '{0}' is not convertible to the appropriate type '{1}' for the {2} option", parameter,
385 ParameterType.GetElementType().Name, DefaultForm));
387 Values.Add(convertedParameter);
388 continue;
391 if (parameter != null)
395 convertedParameter = Convert.ChangeType(parameter, ParameterType);
397 catch(Exception)
399 Console.WriteLine(
400 String.Format("The value '{0}' is not convertible to the appropriate type '{1}' for the {2} option", parameter,
401 ParameterType.Name, DefaultForm));
402 continue;
406 if (MemberInfo is FieldInfo)
408 ((FieldInfo) MemberInfo).SetValue(OptionBundle, convertedParameter);
409 continue;
412 if (MemberInfo is PropertyInfo)
414 ((PropertyInfo) MemberInfo).SetValue(OptionBundle, convertedParameter, null);
415 continue;
418 if ((WhatToDoNext) ((MethodInfo) MemberInfo).Invoke(OptionBundle, new object[] {convertedParameter}) ==
419 WhatToDoNext.AbandonProgram)
420 Environment.Exit(1);
424 private bool IsThisOption(string arg)
426 if (arg != null && arg != string.Empty)
428 arg = arg.TrimStart('-', '/');
429 if (VBCStyleBoolean)
430 arg = arg.TrimEnd('-', '+');
431 return (arg == ShortForm || arg == LongForm || arg == AlternateForm);
433 return false;
436 public static void LinkAlternatesInsideList(ArrayList list)
438 Hashtable baseForms = new Hashtable(list.Count);
439 foreach(OptionDetails option in list)
441 if (option.LongForm != null && option.LongForm.Trim().Length > 0)
443 string[] parts = option.LongForm.Split(':');
444 if (parts.Length < 2)
446 baseForms.Add(option.LongForm, option);
448 else
450 OptionDetails baseForm = (OptionDetails) baseForms[parts[0]];
451 if (baseForm != null)
453 // simple linked list
454 option.NextAlternate = baseForm.NextAlternate;
455 baseForm.NextAlternate = option;
462 private bool IsAlternate(string compoundArg)
464 OptionDetails next = NextAlternate;
465 while(next != null)
467 if (next.IsThisOption(compoundArg))
468 return true;
469 next = next.NextAlternate;
471 return false;
474 public OptionProcessingResult ProcessArgument(string arg, string nextArg)
476 if (IsAlternate(arg + ":" + nextArg))
477 return OptionProcessingResult.NotThisOption;
479 if (IsThisOption(arg))
481 if (!NeedsParameter)
483 if (VBCStyleBoolean && arg.EndsWith("-"))
484 DoIt(false);
485 else
486 DoIt(true);
487 return OptionProcessingResult.OptionAlone;
489 else
491 DoIt(nextArg);
492 return OptionProcessingResult.OptionConsumedParameter;
496 if (IsThisOption(arg + ":" + nextArg))
498 DoIt(true);
499 return OptionProcessingResult.OptionConsumedParameter;
502 return OptionProcessingResult.NotThisOption;