4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
6 // (C) 2002 Rafael Teixeira
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
31 using System
.Collections
;
33 using System
.Reflection
;
35 namespace Mono
.GetOptions
37 public enum WhatToDoNext
43 internal enum OptionProcessingResult
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
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("{");
72 int whereEnds
= shortDescription
.IndexOf("}");
73 if (whereEnds
< whereBegins
)
74 whereEnds
= shortDescription
.Length
+1;
76 paramName
= shortDescription
.Substring(whereBegins
+ 1, whereEnds
- whereBegins
- 1);
78 shortDescription
.Substring(0, whereBegins
) +
80 shortDescription
.Substring(whereEnds
+ 1);
82 return shortDescription
;
85 public string ParamName
93 public static bool Verbose
= false;
95 private OptionsParsingMode parsingMode { get { return this.OptionBundle.ParsingMode; }
}
97 private string linuxLongPrefix
{
99 return (((parsingMode
& OptionsParsingMode
.GNU_DoubleDash
) == OptionsParsingMode
.GNU_DoubleDash
)? "--":"-");
103 public string DefaultForm
106 string shortPrefix
= "-";
107 string longPrefix
= linuxLongPrefix
;
108 if (parsingMode
== OptionsParsingMode
.Windows
) {
112 if (this.ShortForm
!= string.Empty
)
113 return shortPrefix
+this.ShortForm
;
115 return longPrefix
+this.LongForm
;
119 public override string ToString()
122 // TODO: Yet not that good
125 bool hasLongForm
= (this.LongForm
!= null && this.LongForm
!= string.Empty
);
126 if(this.OptionBundle
.ParsingMode
== OptionsParsingMode
.Windows
)
134 longPrefix
= linuxLongPrefix
;
137 optionHelp
+= (this.ShortForm
!= string.Empty
) ? shortPrefix
+this.ShortForm
+" " : " ";
138 optionHelp
+= hasLongForm
? longPrefix
+this.LongForm
: "";
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
+ "]";
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))
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
;
181 this.LongForm
= (option
.LongForm
== string.Empty
)? memberInfo
.Name
:option
.LongForm
;
182 this.AlternateForm
= option
.AlternateForm
;
183 this.ShortDescription
= ExtractParamName(option
.ShortDescription
);
185 this.OptionBundle
= optionBundle
;
186 this.BooleanOption
= false;
187 this.MemberInfo
= memberInfo
;
188 this.NeedsParameter
= false;
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
;
212 if (this.MemberInfo
is MethodInfo
|| this.MemberInfo
is PropertyInfo
)
213 this.MaxOccurs
= option
.MaxOccurs
;
215 throw new InvalidOperationException("MaxOccurs set to non default value (" + option
.MaxOccurs
+ ") for a [" +
216 this.MemberInfo
.ToString() + "] option");
222 this.BooleanOption
= true;
223 if (option
.MaxOccurs
!= 1)
225 if (this.MemberInfo
is MethodInfo
|| this.MemberInfo
is PropertyInfo
)
226 this.MaxOccurs
= option
.MaxOccurs
;
228 throw new InvalidOperationException("MaxOccurs set to non default value (" + option
.MaxOccurs
+ ") for a [" +
229 this.MemberInfo
.ToString() + "] option");
237 get { return this.ShortForm + this.LongForm; }
240 int IComparable
.CompareTo(object other
)
242 return Key
.CompareTo(((OptionDetails
)other
).Key
);
245 public void TransferValues()
249 if (MemberInfo
is FieldInfo
)
251 ((FieldInfo
)MemberInfo
).SetValue(OptionBundle
, Values
.ToArray(ParameterType
.GetElementType()));
255 if (MemberInfo
is PropertyInfo
)
257 ((PropertyInfo
)MemberInfo
).SetValue(OptionBundle
, Values
.ToArray(ParameterType
.GetElementType()), null);
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
)
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
)
281 Console
.WriteLine("<" + this.LongForm
+ "> set to [true]");
283 if (MemberInfo
is FieldInfo
)
285 ((FieldInfo
)MemberInfo
).SetValue(OptionBundle
, setValue
);
288 if (MemberInfo
is PropertyInfo
)
290 ((PropertyInfo
)MemberInfo
).SetValue(OptionBundle
, setValue
, null);
293 if ((WhatToDoNext
)((MethodInfo
)MemberInfo
).Invoke(OptionBundle
, null) == WhatToDoNext
.AbandonProgram
)
294 System
.Environment
.Exit(1);
300 private void DoIt(string parameterValue
)
302 if (parameterValue
== null)
305 string[] parameterValues
= parameterValue
.Split(',');
307 Occurred(parameterValues
.Length
);
309 foreach (string parameter
in parameterValues
)
312 object convertedParameter
= null;
315 Console
.WriteLine("<" + this.LongForm
+ "> set to [" + parameter
+ "]");
317 if (Values
!= null && parameter
!= null) {
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
);
327 if (parameter
!= null) {
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
));
336 if (MemberInfo
is FieldInfo
) {
337 ((FieldInfo
)MemberInfo
).SetValue(OptionBundle
, convertedParameter
);
341 if (MemberInfo
is PropertyInfo
) {
342 ((PropertyInfo
)MemberInfo
).SetValue(OptionBundle
, convertedParameter
, null);
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
);
361 public OptionProcessingResult
ProcessArgument(string arg
, string nextArg
)
363 if (IsThisOption(arg
))
367 DoIt(true); // in preparation for vbc-like booleans
368 return OptionProcessingResult
.OptionAlone
;
373 return OptionProcessingResult
.OptionConsumedParameter
;
377 if (IsThisOption(arg
+ ":" + nextArg
))
380 return OptionProcessingResult
.OptionConsumedParameter
;
383 return OptionProcessingResult
.NotThisOption
;