2004-05-27 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / Mono.GetOptions / OptionDetails.cs
1 //
2 // OptionDetails.cs
3 //
4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
5 //
6 // (C) 2002 Rafael Teixeira
7 //
8
9 using System;
10 using System.Collections;
11 using System.IO;
12 using System.Reflection;
13
14 namespace Mono.GetOptions
15 {
16         public enum WhatToDoNext
17         {
18                 AbandonProgram,
19                 GoAhead
20         }
21         
22         internal enum OptionProcessingResult
23         {
24                 NotThisOption,
25                 OptionAlone,
26                 OptionConsumedParameter
27         }
28
29         internal class OptionDetails : IComparable
30         {
31                 public char ShortForm;
32                 public string LongForm;
33                 public string AlternateForm;
34                 public string ShortDescription;
35                 public bool NeedsParameter;
36                 public int MaxOccurs; // negative means there is no limit
37                 public int Occurs;
38                 public Options OptionBundle;
39                 public MemberInfo MemberInfo;
40                 public ArrayList Values;
41                 public System.Type ParameterType;
42                 public string paramName = null;
43                 public string ParamName 
44                 {
45                         get 
46                         { 
47                                 if (paramName == null)
48                                 {
49                                         int whereBegins = ShortDescription.IndexOf("{");
50                                         if (whereBegins < 0)
51                                                 paramName = "PARAM";
52                                         else
53                                         {
54                                                 int whereEnds = ShortDescription.IndexOf("}");
55                                                 if (whereEnds < whereBegins)
56                                                         whereEnds = ShortDescription.Length+1;
57                                                 
58                                                 paramName = ShortDescription.Substring(whereBegins + 1, whereEnds - whereBegins - 1);
59                                                 ShortDescription = 
60                                                         ShortDescription.Substring(0, whereBegins) + 
61                                                         paramName +
62                                                         ShortDescription.Substring(whereEnds + 1);
63                                         }
64                                 }
65                                 return paramName;
66                         }
67                 }
68                                 
69                 public static bool Verbose = false;
70                 
71                 public override string ToString()
72                 {
73                         string optionHelp;
74                         // TODO: Yet not that good
75                         string shortPrefix;
76                         string longPrefix;
77                         if(this.OptionBundle.ParsingMode == Mono.GetOptions.OptionsParsingMode.Windows)
78                         {
79                                 shortPrefix = "/";
80                                 longPrefix = "/";
81                         } \r
82                         else \r
83                         {
84                                 shortPrefix = "-";
85                                 longPrefix = "--";
86                         }
87                         optionHelp = "  ";
88                         optionHelp += (this.ShortForm != ' ') ? shortPrefix+this.ShortForm+" " : "   ";
89                         optionHelp += (this.LongForm != string.Empty && this.LongForm != null) ? longPrefix+this.LongForm : "";
90                         if (NeedsParameter)
91                         {
92                                 if (this.LongForm != string.Empty && this.LongForm != null)
93                                         optionHelp += ":"; 
94                                 optionHelp += ParamName; 
95                         }
96                         optionHelp = optionHelp.PadRight(32) + " ";
97                         optionHelp += this.ShortDescription;
98                         if (this.AlternateForm != string.Empty && this.AlternateForm != null)
99                                 optionHelp += " [/"+this.AlternateForm + "]";
100                         return optionHelp; 
101                 }
102
103                 private static System.Type TypeOfMember(MemberInfo memberInfo)
104                 {
105                         if ((memberInfo.MemberType == MemberTypes.Field && memberInfo is FieldInfo))
106                                 return ((FieldInfo)memberInfo).FieldType;
107
108                         if ((memberInfo.MemberType == MemberTypes.Property && memberInfo is PropertyInfo))
109                                 return ((PropertyInfo)memberInfo).PropertyType;
110
111                         if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo))
112                         {
113                                 if (((MethodInfo)memberInfo).ReturnType.FullName != typeof(WhatToDoNext).FullName)
114                                         throw new NotSupportedException("Option method must return '" + typeof(WhatToDoNext).FullName + "'");
115
116                                 ParameterInfo[] parameters = ((MethodInfo)memberInfo).GetParameters();
117                                 if ((parameters == null) || (parameters.Length == 0))
118                                         return null;
119                                 else
120                                         return parameters[0].ParameterType;
121                         }
122
123                         throw new NotSupportedException("'" + memberInfo.MemberType + "' memberType is not supported");
124                 }
125
126                 public OptionDetails(MemberInfo memberInfo, OptionAttribute option, Options optionBundle)
127                 {
128                         this.ShortForm = option.ShortForm;
129                         this.LongForm = (option.LongForm == string.Empty)? memberInfo.Name:option.LongForm;
130                         this.AlternateForm = option.AlternateForm;
131                         this.ShortDescription = option.ShortDescription;
132                         this.Occurs = 0;
133                         this.OptionBundle = optionBundle; 
134                         this.MemberInfo = memberInfo;
135                         this.NeedsParameter = false;
136                         this.Values = null;
137                         this.MaxOccurs = 1;
138                         this.ParameterType = TypeOfMember(memberInfo);
139
140                         if (this.ParameterType != null)
141                         {
142                                 if (this.ParameterType.FullName != "System.Boolean")
143                                 {
144                                         if (this.LongForm.IndexOf(':') >= 0)
145                                                 throw new InvalidOperationException("Options with an embedded colon (':') in their visible name must be boolean!!! [" + 
146                                                                         this.MemberInfo.ToString() + " isn't]");
147                                 
148                                         this.NeedsParameter = true;
149
150                                         if (option.MaxOccurs != 1)
151                                         {
152                                                 if (this.ParameterType.IsArray)
153                                                 {
154                                                         this.Values = new ArrayList();
155                                                         this.MaxOccurs = option.MaxOccurs;
156                                                 }
157                                                 else
158                                                 {
159                                                         if (this.MemberInfo is MethodInfo || this.MemberInfo is PropertyInfo)
160                                                                 this.MaxOccurs = option.MaxOccurs;
161                                                         else
162                                                                 throw new InvalidOperationException("MaxOccurs set to non default value (" + option.MaxOccurs + ") for a [" + 
163                                                                                         this.MemberInfo.ToString() + "] option");
164                                                 }
165                                         }
166                                 }
167                                 else
168                                 {
169                                         if (option.MaxOccurs != 1)
170                                         {                       
171                                                 if (this.MemberInfo is MethodInfo || this.MemberInfo is PropertyInfo)
172                                                         this.MaxOccurs = option.MaxOccurs;
173                                                 else
174                                                         throw new InvalidOperationException("MaxOccurs set to non default value (" + option.MaxOccurs + ") for a [" + 
175                                                                                 this.MemberInfo.ToString() + "] option");
176                                         }
177                                 }
178                         }
179                 }
180
181                 internal string Key
182                 {
183                         get { return ((this.ShortForm != ' ') ? this.ShortForm + "" : "") + this.LongForm; }
184                 }
185
186                 int IComparable.CompareTo(object other)
187                 {
188                         return Key.CompareTo(((OptionDetails)other).Key);
189                 }
190
191                 public void TransferValues()
192                 {
193                         if (Values != null)
194                         {
195                                 if (MemberInfo is FieldInfo)
196                                 {
197                                         ((FieldInfo)MemberInfo).SetValue(OptionBundle, Values.ToArray(ParameterType.GetElementType()));
198                                         return;
199                                 }
200
201                                 if (MemberInfo is PropertyInfo) 
202                                 {
203                                         ((PropertyInfo)MemberInfo).SetValue(OptionBundle, Values.ToArray(ParameterType.GetElementType()), null);
204                                         return;
205                                 }
206
207                                 if ((WhatToDoNext)((MethodInfo)MemberInfo).Invoke(OptionBundle, new object[] { Values.ToArray(ParameterType.GetElementType()) }) == WhatToDoNext.AbandonProgram)
208                                         System.Environment.Exit(1);
209                         }
210                 }
211
212                 private void Occurred(int howMany)
213                 {
214                         Occurs += howMany;
215
216                         if (MaxOccurs > 0 && Occurs > MaxOccurs)
217                                 throw new IndexOutOfRangeException("Option " + ShortForm + " can be used at most " + MaxOccurs + " times");
218                 }
219
220                 private void DoIt()
221                 {
222                         if (!NeedsParameter)
223                         {
224                                 Occurred(1);
225
226                                 if (Verbose)
227                                         Console.WriteLine("<" + this.LongForm + "> set to [true]");
228
229                                 if (MemberInfo is FieldInfo)
230                                 {
231                                         ((FieldInfo)MemberInfo).SetValue(OptionBundle, true);
232                                         return;
233                                 }
234                                 if (MemberInfo is PropertyInfo)
235                                 {
236                                         ((PropertyInfo)MemberInfo).SetValue(OptionBundle, true, null);
237                                         return;
238                                 }
239                                 if ((WhatToDoNext)((MethodInfo)MemberInfo).Invoke(OptionBundle, null) == WhatToDoNext.AbandonProgram)
240                                         System.Environment.Exit(1);
241
242                                 return;
243                         }
244                 }
245                 
246                 private void DoIt(string parameterValue)
247                 {
248                         if (parameterValue == null)
249                                 parameterValue = "";
250
251                         string[] parameterValues = parameterValue.Split(',');
252
253                         Occurred(parameterValues.Length);
254
255                         foreach (string parameter in parameterValues)
256                         {
257
258                                 object convertedParameter = null;
259
260                                 if (Verbose)
261                                         Console.WriteLine("<" + this.LongForm + "> set to [" + parameter + "]");
262
263                                 if (Values != null && parameter != null)
264                                 {
265                                         convertedParameter = Convert.ChangeType(parameter, ParameterType.GetElementType());
266                                         Values.Add(convertedParameter);
267                                         continue;
268                                 }
269
270                                 if (parameter != null)
271                                         convertedParameter = Convert.ChangeType(parameter, ParameterType);
272
273                                 if (MemberInfo is FieldInfo)
274                                 {
275                                         ((FieldInfo)MemberInfo).SetValue(OptionBundle, convertedParameter);
276                                         continue;
277                                 }
278
279                                 if (MemberInfo is PropertyInfo)
280                                 {
281                                         ((PropertyInfo)MemberInfo).SetValue(OptionBundle, convertedParameter, null);
282                                         continue;
283                                 }
284
285                                 if ((WhatToDoNext)((MethodInfo)MemberInfo).Invoke(OptionBundle, new object[] { convertedParameter }) == WhatToDoNext.AbandonProgram)
286                                         System.Environment.Exit(1);
287                         }
288                 }
289
290                 private bool StartsLikeAnOption(string arg)
291                 {
292                         return (arg != null && arg[0] == '-');
293                 }
294
295                 private bool IsThisOption(string arg)
296                 {
297                         if (StartsLikeAnOption(arg))
298                         {
299                                 arg = arg.TrimStart('-');
300                                 return (arg == "" + ShortForm || arg == LongForm || arg == AlternateForm);
301                         }
302                         return false;
303                 }
304
305                 public OptionProcessingResult ProcessArgument(string arg, string nextArg)
306                 {
307                         if (IsThisOption(arg))
308                         {
309                                 if (!NeedsParameter)
310                                 {
311                                         DoIt();
312                                         return OptionProcessingResult.OptionAlone;
313                                 }
314                                 else
315                                 {
316                                         if (StartsLikeAnOption(nextArg))
317                                         {
318                                                 DoIt(null);
319                                                 return OptionProcessingResult.OptionAlone;
320                                         }
321                                         else
322                                         {
323                                                 DoIt(nextArg);
324                                                 return OptionProcessingResult.OptionConsumedParameter;
325                                         }
326                                 }
327                         }
328
329                         if (IsThisOption(arg + ":" + nextArg))
330                         {
331                                 DoIt();
332                                 return OptionProcessingResult.OptionConsumedParameter;
333                         }
334
335                         return OptionProcessingResult.NotThisOption;
336                 }
337         }
338 }