Jeffrey Morlan (mrnobo1024 at yahoo.com) - Fix ModifyWorldTransform multiplies. See...
[reactos.git] / irc / TechBot / TechBot.Library / ParametersParser.cs
1 using System;
2 using System.Text.RegularExpressions;
3
4 //Code taken from : http://www.codeproject.com/KB/recipes/commandlineparser.aspx
5
6 namespace TechBot.Library
7 {
8 /// <summary>Implementation of a command-line parsing class. Is capable of
9 /// having switches registered with it directly or can examine a registered
10 /// class for any properties with the appropriate attributes appended to
11 /// them.</summary>
12 public class ParametersParser
13 {
14 /// <summary>A simple internal class for passing back to the caller
15 /// some information about the switch. The internals/implementation
16 /// of this class has privillaged access to the contents of the
17 /// SwitchRecord class.</summary>
18 public class SwitchInfo
19 {
20 #region Private Variables
21 private object m_Switch = null;
22 #endregion
23
24 #region Public Properties
25 public string Name { get { return (m_Switch as SwitchRecord).Name; } }
26 public string Description { get { return (m_Switch as SwitchRecord).Description; } }
27 public string[] Aliases { get { return (m_Switch as SwitchRecord).Aliases; } }
28 public System.Type Type { get { return (m_Switch as SwitchRecord).Type; } }
29 public object Value { get { return (m_Switch as SwitchRecord).Value; } }
30 public object InternalValue { get { return (m_Switch as SwitchRecord).InternalValue; } }
31 public bool IsEnum { get { return (m_Switch as SwitchRecord).Type.IsEnum; } }
32 public string[] Enumerations { get { return (m_Switch as SwitchRecord).Enumerations; } }
33 #endregion
34
35 /// <summary>
36 /// Constructor for the SwitchInfo class. Note, in order to hide to the outside world
37 /// information not necessary to know, the constructor takes a System.Object (aka
38 /// object) as it's registering type. If the type isn't of the correct type, an exception
39 /// is thrown.
40 /// </summary>
41 /// <param name="rec">The SwitchRecord for which this class store information.</param>
42 /// <exception cref="ArgumentException">Thrown if the rec parameter is not of
43 /// the type SwitchRecord.</exception>
44 public SwitchInfo( object rec )
45 {
46 if ( rec is SwitchRecord )
47 m_Switch = rec;
48 else
49 throw new ArgumentException();
50 }
51 }
52
53 /// <summary>
54 /// The SwitchRecord is stored within the parser's collection of registered
55 /// switches. This class is private to the outside world.
56 /// </summary>
57 private class SwitchRecord
58 {
59 #region Private Variables
60 private string m_name = "";
61 private string m_description = "";
62 private object m_value = null;
63 private System.Type m_switchType = typeof(bool);
64 private System.Collections.ArrayList m_Aliases = null;
65 private string m_Pattern = "";
66
67 // The following advanced functions allow for callbacks to be
68 // made to manipulate the associated data type.
69 private System.Reflection.MethodInfo m_SetMethod = null;
70 private System.Reflection.MethodInfo m_GetMethod = null;
71 private object m_PropertyOwner = null;
72 #endregion
73
74 #region Private Utility Functions
75 private void Initialize( string name, string description )
76 {
77 m_name = name;
78 m_description = description;
79
80 BuildPattern();
81 }
82
83 private void BuildPattern()
84 {
85 string matchString = Name;
86
87 if ( Aliases != null && Aliases.Length > 0 )
88 foreach( string s in Aliases )
89 matchString += "|" + s;
90
91 string strPatternStart = @"(\s|^)(?<match>(-{1,2}|/)(";
92 string strPatternEnd; // To be defined below.
93
94 // The common suffix ensures that the switches are followed by
95 // a white-space OR the end of the string. This will stop
96 // switches such as /help matching /helpme
97 //
98 string strCommonSuffix = @"(?=(\s|$))";
99
100 if ( Type == typeof(bool) )
101 strPatternEnd = @")(?<value>(\+|-){0,1}))";
102 else if ( Type == typeof(string) )
103 strPatternEnd = @")(?::|\s+))((?:"")(?<value>.+)(?:"")|(?<value>\S+))";
104 else if ( Type == typeof(int) )
105 strPatternEnd = @")(?::|\s+))((?<value>(-|\+)[0-9]+)|(?<value>[0-9]+))";
106 else if ( Type.IsEnum )
107 {
108 string[] enumNames = Enumerations;
109 string e_str = enumNames[0];
110 for ( int e=1; e<enumNames.Length; e++ )
111 e_str += "|" + enumNames[e];
112 strPatternEnd = @")(?::|\s+))(?<value>" + e_str + @")";
113 }
114 else
115 throw new System.ArgumentException();
116
117 // Set the internal regular expression pattern.
118 m_Pattern = strPatternStart + matchString + strPatternEnd + strCommonSuffix;
119 }
120 #endregion
121
122 #region Public Properties
123 public object Value
124 {
125 get
126 {
127 if ( ReadValue != null )
128 return ReadValue;
129 else
130 return m_value;
131 }
132 }
133
134 public object InternalValue
135 {
136 get { return m_value; }
137 }
138
139 public string Name
140 {
141 get { return m_name; }
142 set { m_name = value; }
143 }
144
145 public string Description
146 {
147 get { return m_description; }
148 set { m_description = value; }
149 }
150
151 public System.Type Type
152 {
153 get { return m_switchType; }
154 }
155
156 public string[] Aliases
157 {
158 get { return (m_Aliases != null) ? (string[])m_Aliases.ToArray(typeof(string)): null; }
159 }
160
161 public string Pattern
162 {
163 get { return m_Pattern; }
164 }
165
166 public System.Reflection.MethodInfo SetMethod
167 {
168 set { m_SetMethod = value; }
169 }
170
171 public System.Reflection.MethodInfo GetMethod
172 {
173 set { m_GetMethod = value; }
174 }
175
176 public object PropertyOwner
177 {
178 set { m_PropertyOwner = value; }
179 }
180
181 public object ReadValue
182 {
183 get
184 {
185 object o = null;
186 if ( m_PropertyOwner != null && m_GetMethod != null )
187 o = m_GetMethod.Invoke( m_PropertyOwner, null );
188 return o;
189 }
190 }
191
192 public string[] Enumerations
193 {
194 get
195 {
196 if ( m_switchType.IsEnum )
197 return System.Enum.GetNames( m_switchType );
198 else
199 return null;
200 }
201 }
202 #endregion
203
204 #region Constructors
205 public SwitchRecord( string name, string description )
206 {
207 Initialize( name, description );
208 }
209
210 public SwitchRecord( string name, string description, System.Type type )
211 {
212 if ( type == typeof(bool) ||
213 type == typeof(string) ||
214 type == typeof(int) ||
215 type.IsEnum )
216 {
217 m_switchType = type;
218 Initialize( name, description );
219 }
220 else
221 throw new ArgumentException("Currently only Ints, Bool and Strings are supported");
222 }
223 #endregion
224
225 #region Public Methods
226 public void AddAlias( string alias )
227 {
228 if ( m_Aliases == null )
229 m_Aliases = new System.Collections.ArrayList();
230 m_Aliases.Add( alias );
231
232 BuildPattern();
233 }
234
235 public void Notify( object value )
236 {
237 if ( m_PropertyOwner != null && m_SetMethod != null )
238 {
239 object[] parameters = new object[1];
240 parameters[0] = value;
241 m_SetMethod.Invoke( m_PropertyOwner, parameters );
242 }
243 m_value = value;
244 }
245
246 #endregion
247 }
248
249
250 #region Private Variables
251 private string m_commandLine = "";
252 private string m_workingString = "";
253 private string m_applicationName = "";
254 private string[] m_splitParameters = null;
255 private System.Collections.ArrayList m_switches = null;
256 #endregion
257
258 #region Private Utility Functions
259 private void ExtractApplicationName()
260 {
261 Regex r = new Regex(@"^(?<commandLine>("".+""|(\S)+))(?<remainder>.+)",
262 RegexOptions.ExplicitCapture);
263 Match m = r.Match(m_commandLine);
264 if ( m != null && m.Groups["commandLine"] != null )
265 {
266 m_applicationName = m.Groups["commandLine"].Value;
267 m_workingString = m.Groups["remainder"].Value;
268 }
269 }
270
271 private void SplitParameters()
272 {
273 // Populate the split parameters array with the remaining parameters.
274 // Note that if quotes are used, the quotes are removed.
275 // e.g. one two three "four five six"
276 // 0 - one
277 // 1 - two
278 // 2 - three
279 // 3 - four five six
280 // (e.g. 3 is not in quotes).
281 Regex r = new Regex(@"((\s*(""(?<param>.+?)""|(?<param>\S+))))",
282 RegexOptions.ExplicitCapture);
283 MatchCollection m = r.Matches( m_workingString );
284
285 if ( m != null )
286 {
287 m_splitParameters = new string[ m.Count ];
288 for ( int i=0; i < m.Count; i++ )
289 m_splitParameters[i] = m[i].Groups["param"].Value;
290 }
291 }
292
293 private void HandleSwitches()
294 {
295 if ( m_switches != null )
296 {
297 foreach ( SwitchRecord s in m_switches )
298 {
299 Regex r = new Regex( s.Pattern,
300 RegexOptions.ExplicitCapture
301 | RegexOptions.IgnoreCase );
302 MatchCollection m = r.Matches( m_workingString );
303 if ( m != null )
304 {
305 for ( int i=0; i < m.Count; i++ )
306 {
307 string value = null;
308 if ( m[i].Groups != null && m[i].Groups["value"] != null )
309 value = m[i].Groups["value"].Value;
310
311 if ( s.Type == typeof(bool))
312 {
313 bool state = true;
314 // The value string may indicate what value we want.
315 if ( m[i].Groups != null && m[i].Groups["value"] != null )
316 {
317 switch ( value )
318 {
319 case "+": state = true;
320 break;
321 case "-": state = false;
322 break;
323 case "": if ( s.ReadValue != null )
324 state = !(bool)s.ReadValue;
325 break;
326 default: break;
327 }
328 }
329 s.Notify( state );
330 break;
331 }
332 else if ( s.Type == typeof(string) )
333 s.Notify( value );
334 else if ( s.Type == typeof(int) )
335 s.Notify( int.Parse( value ) );
336 else if ( s.Type.IsEnum )
337 s.Notify( System.Enum.Parse(s.Type,value,true) );
338 }
339 }
340
341 m_workingString = r.Replace(m_workingString, " ");
342 }
343 }
344 }
345
346 #endregion
347
348 #region Public Properties
349 public string ApplicationName
350 {
351 get { return m_applicationName; }
352 }
353
354 public string[] Parameters
355 {
356 get { return m_splitParameters; }
357 }
358
359 public SwitchInfo[] Switches
360 {
361 get
362 {
363 if ( m_switches == null )
364 return null;
365 else
366 {
367 SwitchInfo[] si = new SwitchInfo[ m_switches.Count ];
368 for ( int i=0; i<m_switches.Count; i++ )
369 si[i] = new SwitchInfo( m_switches[i] );
370 return si;
371 }
372 }
373 }
374
375 public object this[string name]
376 {
377 get
378 {
379 if ( m_switches != null )
380 for ( int i=0; i<m_switches.Count; i++ )
381 if ( string.Compare( (m_switches[i] as SwitchRecord).Name, name, true )==0 )
382 return (m_switches[i] as SwitchRecord).Value;
383 return null;
384 }
385 }
386
387 /// <summary>This function returns a list of the unhandled switches
388 /// that the parser has seen, but not processed.</summary>
389 /// <remark>The unhandled switches are not removed from the remainder
390 /// of the command-line.</remark>
391 public string[] UnhandledSwitches
392 {
393 get
394 {
395 string switchPattern = @"(\s|^)(?<match>(-{1,2}|/)(.+?))(?=(\s|$))";
396 Regex r = new Regex( switchPattern,
397 RegexOptions.ExplicitCapture
398 | RegexOptions.IgnoreCase );
399 MatchCollection m = r.Matches( m_workingString );
400
401 if ( m != null )
402 {
403 string[] unhandled = new string[ m.Count ];
404 for ( int i=0; i < m.Count; i++ )
405 unhandled[i] = m[i].Groups["match"].Value;
406 return unhandled;
407 }
408 else
409 return null;
410 }
411 }
412 #endregion
413
414 #region Public Methods
415 public void AddSwitch( string name, string description )
416 {
417 if ( m_switches == null )
418 m_switches = new System.Collections.ArrayList();
419
420 SwitchRecord rec = new SwitchRecord( name, description );
421 m_switches.Add( rec );
422 }
423
424 public void AddSwitch( string[] names, string description )
425 {
426 if ( m_switches == null )
427 m_switches = new System.Collections.ArrayList();
428 SwitchRecord rec = new SwitchRecord( names[0], description );
429 for ( int s=1; s<names.Length; s++ )
430 rec.AddAlias( names[s] );
431 m_switches.Add( rec );
432 }
433
434 public bool Parse()
435 {
436 ExtractApplicationName();
437
438 // Remove switches and associated info.
439 HandleSwitches();
440
441 // Split parameters.
442 SplitParameters();
443
444 return true;
445 }
446
447 public object InternalValue(string name)
448 {
449 if ( m_switches != null )
450 for ( int i=0; i<m_switches.Count; i++ )
451 if ( string.Compare( (m_switches[i] as SwitchRecord).Name, name, true )==0 )
452 return (m_switches[i] as SwitchRecord).InternalValue;
453 return null;
454 }
455 #endregion
456
457 #region Constructors
458 public ParametersParser( string commandLine )
459 {
460 m_commandLine = commandLine;
461 }
462
463 public ParametersParser( string commandLine,
464 object classForAutoAttributes )
465 {
466 m_commandLine = commandLine;
467
468 Type type = classForAutoAttributes.GetType();
469 System.Reflection.MemberInfo[] members = type.GetMembers();
470
471 for(int i=0; i<members.Length; i++)
472 {
473 object[] attributes = members[i].GetCustomAttributes(false);
474 if(attributes.Length > 0)
475 {
476 SwitchRecord rec = null;
477
478 foreach ( Attribute attribute in attributes )
479 {
480 if ( attribute is CommandParameterAttribute )
481 {
482 CommandParameterAttribute switchAttrib =
483 (CommandParameterAttribute) attribute;
484
485 // Get the property information. We're only handling
486 // properties at the moment!
487 if ( members[i] is System.Reflection.PropertyInfo )
488 {
489 System.Reflection.PropertyInfo pi = (System.Reflection.PropertyInfo) members[i];
490
491 rec = new SwitchRecord( switchAttrib.Name,
492 switchAttrib.Description,
493 pi.PropertyType );
494
495 // Map in the Get/Set methods.
496 rec.SetMethod = pi.GetSetMethod();
497 rec.GetMethod = pi.GetGetMethod();
498 rec.PropertyOwner = classForAutoAttributes;
499
500 // Can only handle a single switch for each property
501 // (otherwise the parsing of aliases gets silly...)
502 break;
503 }
504 }
505 }
506
507 // See if any aliases are required. We can only do this after
508 // a switch has been registered and the framework doesn't make
509 // any guarantees about the order of attributes, so we have to
510 // walk the collection a second time.
511 if ( rec != null )
512 {
513 foreach ( Attribute attribute in attributes )
514 {
515 if (attribute is CommandParameterAliasAttribute)
516 {
517 CommandParameterAliasAttribute aliasAttrib =
518 (CommandParameterAliasAttribute)attribute;
519 rec.AddAlias( aliasAttrib.Alias );
520 }
521 }
522 }
523
524 // Assuming we have a switch record (that may or may not have
525 // aliases), add it to the collection of switches.
526 if ( rec != null )
527 {
528 if ( m_switches == null )
529 m_switches = new System.Collections.ArrayList();
530 m_switches.Add( rec );
531 }
532 }
533 }
534 }
535 #endregion
536 }
537 }