Advertisement
Guest User

Untitled

a guest
Apr 26th, 2015
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.28 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Diagnostics;
  5. using System.Globalization;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Text.RegularExpressions;
  9. using ArgumentParser.Arguments;
  10. using ArgumentParser.Factory;
  11.  
  12. namespace ArgumentParser
  13. {
  14. public static class Parser
  15. {
  16. #region Constants
  17. internal const String PREFIX_UNIX_LONG = "--";
  18. internal const String PREFIX_UNIX_SHORT = "-";
  19. internal const String PREFIX_WINDOWS = "/";
  20.  
  21. #if DEBUG
  22. private const String VERB_PATTERN = @"
  23. (?<=\s|^)
  24. (?:
  25. (?<args>[\-/]+\b.*)|
  26. (
  27. (?>"" (?> \\. | [^""] )* "")|
  28. (?>' (?> \\. | [^'] )* ')|
  29. (?> \\. | [^\s""'] )+
  30. )
  31. (?=\s|$)
  32. )";
  33.  
  34. private const String UNIX_PARAMETERS_PATTERN = @"
  35. (^|\s)
  36. (
  37. (?<prefix>--)
  38. (
  39. (?<tag>(?!-)[\w\-]+)\s+(?!-)
  40. (?<value>
  41. (
  42. (?> "" (?> \\. | [^""])* "")|
  43. (?> ' (?> \\. | [^'])* ')|
  44. (?> \\. | [^\-""'] )*
  45. )(?=\s|$)
  46. )|
  47. (?<tag>(?!-)[\w\-]+)($|(?=\s))
  48. )|
  49. (?<prefix>-)
  50. (?<tag>\w)\s+
  51. (?<value>
  52. (
  53. (?> "" (?> \\. | [^""])* "")|
  54. (?> ' (?> \\. | [^'])* ')|
  55. (?> \\. | [^\-""'] )*
  56. )(?=\s|$)
  57. )|
  58. (?<prefix>-)
  59. (?<tag>\w)+($|(?=\s))
  60. )";
  61.  
  62. private const String WINDOWS_PARAMETERS_PATTERN = @"
  63. (^|\s)(?<prefix>/)
  64. (
  65. (?<tag>\w+)\s+(?=[^/])
  66. (?<value>
  67. (?>"" (?> \\. | [^""])* "")|
  68. (?> ' (?> \\. | [^'])* ')|
  69. (?> \\. | [^\s""'] )*
  70. )|
  71. (?<tag>\w+)(?!/)
  72. )";
  73. #else
  74. /* Minified version of the constants */
  75. #endif
  76. #endregion
  77.  
  78. public static IEnumerable<RawParameter> GetRawArguments(String input, ParameterTokenStyle tokenStyle)
  79. {
  80. MatchCollection matches = Regex.Matches(
  81. input: input,
  82. pattern: tokenStyle == ParameterTokenStyle.POSIX
  83. ? (UNIX_PARAMETERS_PATTERN)
  84. : (WINDOWS_PARAMETERS_PATTERN),
  85. options: RegexOptions.ExplicitCapture |
  86. RegexOptions.IgnorePatternWhitespace |
  87. RegexOptions.CultureInvariant |
  88. RegexOptions.Singleline);
  89.  
  90. return matches.OfType<Match>().Select(x =>
  91. new RawParameter(
  92. x.Groups["prefix"].Value,
  93. x.Groups["tag"].Value,
  94. x.Groups["value"].Value));
  95. }
  96.  
  97. public static void GetParts(ParserOptions options, String input, out String[] verbs, out String arguments)
  98. {
  99. var matches = Regex.Matches(
  100. input: input,
  101. pattern: VERB_PATTERN,
  102. options: RegexOptions.IgnorePatternWhitespace |
  103. RegexOptions.CultureInvariant |
  104. RegexOptions.Singleline).OfType<Match>().ToArray();
  105.  
  106. verbs = matches
  107. .Where(x => !x.Groups["args"].Success)
  108. .Select(x => x.Value).ToArray();
  109.  
  110. arguments = matches
  111. .Where(x => x.Groups["args"].Success)
  112. .Select(x => x.Value)
  113. .FirstOrDefault();
  114. }
  115.  
  116. public static void Parse(this IParserContext context, String input)
  117. {
  118. if (context == null)
  119. throw new ArgumentNullException("context");
  120.  
  121. if (context.Options == null)
  122. throw new InvalidOperationException("The provided context does not hold a valid options provider.");
  123.  
  124. String[] verbNames;
  125. String args;
  126. GetParts(context.Options, input, out verbNames, out args);
  127.  
  128. if (!verbNames.Any())
  129. ParseArguments(context, input, context.Options);
  130. else
  131. {
  132. List<String> unmatchedVerbs = verbNames.ToList();
  133. KeyValuePair<PropertyDescriptor, Verb>? verb = null;
  134. Object parent = context;
  135. var enumerator = verbNames.GetEnumerator();
  136.  
  137. Object value = parent;
  138. while (enumerator.MoveNext()
  139. && (verb = GetVerb(parent = value, (String) enumerator.Current)).HasValue
  140. && unmatchedVerbs.Remove((String) enumerator.Current))
  141. {
  142. value = verb.Value.Key.GetValue(parent);
  143. if (value == null)
  144. verb.Value.Key.SetValue(parent, value = Activator.CreateInstance(verb.Value.Key.PropertyType));
  145. }
  146.  
  147. if (!verb.HasValue)
  148. {
  149. ((IVerb) parent).Init(unmatchedVerbs.ToArray());
  150. return;
  151. }
  152.  
  153. // TODO: check for inheritance
  154. ParseArguments((IVerb) verb.Value.Key.GetValue(parent), input, context.Options);
  155. }
  156. }
  157.  
  158. private static KeyValuePair<PropertyDescriptor, Verb>? GetVerb(Object instance, String name)
  159. {
  160. var entry = TypeDescriptor.GetProperties(instance)
  161. .OfType<PropertyDescriptor>()
  162. .Select(x => new
  163. {
  164. Attribute = x.Attributes.OfType<VerbAttribute>().SingleOrDefault(a => a.Tag == name),
  165. Property = x
  166. })
  167. .SingleOrDefault(x => x.Attribute != null);
  168.  
  169. if (entry == null)
  170. return null;
  171.  
  172. return new KeyValuePair<PropertyDescriptor, Verb>(entry.Property, new Verb(entry.Attribute.Tag, entry.Attribute.Description));
  173. }
  174.  
  175. public static IEnumerable<IPairable> GetArguments(String input, ParserOptions options, params IArgument[] arguments)
  176. {
  177. return GetArguments(input, options, arguments as IEnumerable<IArgument>);
  178. }
  179.  
  180. public static IEnumerable<IPairable> GetArguments(String input, ParserOptions options, IEnumerable<IArgument> arguments)
  181. {
  182. if (input == null)
  183. throw new ArgumentNullException("input");
  184.  
  185. if (options == null)
  186. throw new ArgumentNullException("options");
  187.  
  188. if (arguments == null)
  189. throw new ArgumentNullException("arguments");
  190.  
  191. MatchCollection matches = Regex.Matches(
  192. input: input,
  193. pattern: options.TokenStyle == ParameterTokenStyle.POSIX
  194. ? (UNIX_PARAMETERS_PATTERN)
  195. : (WINDOWS_PARAMETERS_PATTERN), // TODO: add support for Windows variants
  196. options: RegexOptions.ExplicitCapture |
  197. RegexOptions.IgnorePatternWhitespace |
  198. RegexOptions.CultureInvariant |
  199. RegexOptions.Singleline);
  200.  
  201. // TODO: migrate the decoupling logic to prevent premature pairing
  202. var matchElements =
  203. matches.OfType<Match>().SelectMany(x => x.Groups["tag"].Captures
  204. .OfType<Capture>()
  205. .GroupBy(c => c.Value, (c, e) => new RawParameter(x.Groups["prefix"].Value, c, x.Groups["value"].Success ? x.Groups["value"].Value : null, e.Count())))
  206. .ToArray();
  207.  
  208. var pairs = arguments
  209. .GroupJoin(
  210. matchElements,
  211. a => a,
  212. p => p,
  213. (a, p) =>
  214. {
  215. var parameters = p.ToArray();
  216. var values = parameters.Select(x => x.Value == null ? null : ParseValue(options, a, x)).ToArray();
  217. var flag = a as IFlag;
  218.  
  219. if (flag != null)
  220. return GetFlagPair(options, flag, parameters, values);
  221.  
  222. return new ParameterPair(a, values);
  223. },
  224. options.PairEqualityComparer)
  225. .GroupBy(x => x.Key, (k, e) => new ParameterGroup(e, k.Prefix, k.Tag)).ToArray();
  226.  
  227. if (options.IgnoreUndefinedParameters)
  228. return pairs;
  229.  
  230. // Caveat: Except() conflates duplicate (unrecognized) parameters.
  231. return pairs.Concat(matchElements.Except(pairs, options.PairEqualityComparer));
  232. }
  233.  
  234. private static FlagPair GetFlagPair(ParserOptions options, IFlag flag, RawParameter[] parameters, Object[] values)
  235. {
  236. int count;
  237. bool aggregateEntries = (flag.Options & FlagOptions.Aggregate) != 0;
  238. bool aggregateValues = (flag.Options & FlagOptions.AggregateExplicit) != 0;
  239.  
  240. if ((flag.Options & FlagOptions.BitField) != 0)
  241. {
  242. if (aggregateEntries)
  243. count = parameters.Aggregate(0, (c, x) =>
  244. {
  245. int value;
  246. if (aggregateValues)
  247. return c + (Int32.TryParse((String) x.Value, NumberStyles.Integer, options.Culture, out value) ? value : 0); // Count invalid entries as '0'
  248.  
  249. return c + (1 << (x.Count - 1));
  250. });
  251. else
  252. count = (1 << ((int?) values.FirstOrDefault(x => x != null)) >> 1) ?? (parameters.Any() ? parameters.First().Count : 0);
  253. }
  254. else
  255. {
  256. if (aggregateEntries)
  257. count = parameters.Aggregate(0, (c, x) =>
  258. {
  259. int value;
  260. if (aggregateValues)
  261. return c + (Int32.TryParse((String) x.Value, NumberStyles.Integer, options.Culture, out value) ? value : 0); // Count invalid entries as '0'
  262.  
  263. return c + x.Count;
  264. });
  265. else
  266. count = (int?) values.FirstOrDefault(x => x != null) ?? (parameters.Any() ? parameters.First().Count : 0);
  267. }
  268.  
  269. return new FlagPair(
  270. argument: flag,
  271. values: values,
  272. count: count);
  273. }
  274.  
  275. private static Object ParseValue(ParserOptions options, IArgument argument, RawParameter parameter)
  276. {
  277. try
  278. {
  279. return options.Detokenize
  280. ? argument.GetValue(options.Culture, options.Detokenizer == null
  281. ? Regex.Unescape((String) parameter.Value ?? String.Empty)
  282. : options.Detokenizer((String) parameter.Value))
  283. : argument.GetValue(options.Culture, (String) parameter.Value);
  284. }
  285. catch (Exception ex)
  286. {
  287. if (options.ExceptionHandler == null || !options.ExceptionHandler.Invoke(ex, argument, parameter))
  288. throw;
  289. }
  290.  
  291. return null;
  292. }
  293.  
  294. public static void ParseArguments(IVerb instance, String input, ParserOptions options)
  295. {
  296. if (instance == null)
  297. throw new ArgumentNullException("instance");
  298.  
  299. if (options == null)
  300. throw new ArgumentNullException("options");
  301.  
  302. ParseArguments(TypeDescriptor.GetProperties(instance).OfType<PropertyDescriptor>(), instance, input, options);
  303. }
  304.  
  305. private static void ParseArguments(IEnumerable<PropertyDescriptor> properties, IVerb instance, String input, ParserOptions options)
  306. {
  307. var methods = instance.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod);
  308. var members = methods.Concat<Object>(properties);
  309. Dictionary<IArgument, Tuple<Object, IOptionAttribute>> arguments;
  310.  
  311. switch (options.TokenStyle)
  312. {
  313. case ParameterTokenStyle.POSIX:
  314. arguments = members
  315. .SelectMany(m => (m is PropertyDescriptor
  316. ? ((PropertyDescriptor) m).Attributes.OfType<POSIXOptionAttribute>()
  317. : ((MethodInfo) m).GetCustomAttributes<POSIXOptionAttribute>()).Select(a => new
  318. {
  319. Member = m,
  320. Attribute = a
  321. }))
  322. .Where(x => x.Attribute != null)
  323. .ToDictionary(
  324. keySelector: x => GetPOSIXArgument(x.Attribute, x.Member),
  325. elementSelector: x => Tuple.Create(x.Member, (IOptionAttribute) x.Attribute));
  326. break;
  327. case ParameterTokenStyle.Windows:
  328. arguments = members
  329. .SelectMany(m => (m is PropertyDescriptor
  330. ? ((PropertyDescriptor) m).Attributes.OfType<WindowsOptionAttribute>()
  331. : ((MethodInfo) m).GetCustomAttributes<WindowsOptionAttribute>()).Select(a => new
  332. {
  333. Member = m,
  334. Attribute = a
  335. }))
  336. .Where(x => x.Attribute != null)
  337. .ToDictionary(
  338. keySelector: x => GetWindowsArgument(x.Attribute, x.Member),
  339. elementSelector: x => Tuple.Create(x.Member, (IOptionAttribute) x.Attribute));
  340. break;
  341. default:
  342. throw new InvalidOperationException("The logic for the provided token style is not defined.");
  343. }
  344.  
  345. var pairs = GetArguments(input, options, arguments.Select(x => x.Key)).ToArray();
  346. var matches = pairs.OfType<ParameterGroup>()
  347. .GroupJoin(
  348. arguments,
  349. x => x,
  350. x => x.Key,
  351. (pair, kv) => new KeyValuePair<Tuple<Object, IOptionAttribute>, ParameterPair[]>(kv.First().Value, pair.ToArray()),
  352. options.PairEqualityComparer);
  353.  
  354. var parameters = pairs.OfType<RawParameter>();
  355. foreach (var parameter in parameters)
  356. instance.HandleParameter(parameter);
  357.  
  358. BindValues(instance, matches);
  359. }
  360.  
  361. private static IArgument GetWindowsArgument(WindowsOptionAttribute attribute, Object member)
  362. {
  363. // TODO: implement Windows flags
  364.  
  365. return WindowsArgumentFactory.CreateArgument(
  366. tag: attribute.Tag,
  367. description: attribute.Description,
  368. returnType: member is PropertyDescriptor
  369. ? ((PropertyDescriptor) member).PropertyType
  370. : ((MethodInfo) member).GetParameters().First().ParameterType,
  371. typeConverter: attribute.TypeConverter,
  372. defaultValue: attribute.DefaultValue);
  373. }
  374.  
  375. private static IArgument GetPOSIXArgument(POSIXOptionAttribute attribute, Object member)
  376. {
  377. var descriptor = member as PropertyDescriptor;
  378. var returnType = descriptor != null
  379. ? descriptor.PropertyType
  380. : ((MethodInfo) member).GetParameters().First().ParameterType;
  381.  
  382. if (!attribute.IsShort)
  383. return POSIXArgumentFactory.CreateArgument(
  384. tag: attribute.Tag,
  385. description: attribute.Description,
  386. returnType: returnType,
  387. typeConverter: attribute.TypeConverter,
  388. defaultValue: attribute.DefaultValue);
  389.  
  390. var flagAttribute = attribute as POSIXFlagAttribute;
  391.  
  392. if (flagAttribute != null)
  393. return POSIXArgumentFactory.CreateFlag(
  394. tag: flagAttribute.Tag.First(), // The "Tag" property should hold a single character.
  395. description: flagAttribute.Description,
  396. returnType: returnType,
  397. options: flagAttribute.Options,
  398. typeConverter: flagAttribute.TypeConverter,
  399. defaultValue: flagAttribute.DefaultValue);
  400.  
  401. return POSIXArgumentFactory.CreateArgument(
  402. tag: attribute.Tag.First(),
  403. description: attribute.Description,
  404. returnType: returnType,
  405. typeConverter: attribute.TypeConverter,
  406. defaultValue: attribute.DefaultValue);
  407. }
  408.  
  409. private static void BindValues(Object instance, IEnumerable<KeyValuePair<Tuple<Object, IOptionAttribute>, ParameterPair[]>> matches)
  410. {
  411. foreach (var binding in matches)
  412. {
  413. var attribute = binding.Key.Item2;
  414. var property = binding.Key.Item1 as PropertyDescriptor;
  415. var methodInfo = binding.Key.Item1 as MethodInfo;
  416.  
  417. foreach (var pair in binding.Value)
  418. {
  419. var flagPair = pair as FlagPair;
  420.  
  421. if (pair.Matched)
  422. {
  423. if (flagPair != null)
  424. {
  425. BindFlag(instance, flagPair, property, methodInfo, attribute, pair);
  426. }
  427. else
  428. {
  429. if (property != null)
  430. foreach (var value in pair.Values)
  431. property.SetValue(instance, value);
  432. else if (attribute.ManualBinding)
  433. methodInfo.Invoke(instance, new Object[] { null, new BindingEventArgs(pair, attribute) });
  434. else
  435. foreach (var value in pair.Values)
  436. methodInfo.Invoke(instance, new[] { value });
  437. }
  438. }
  439. else if (pair.Argument.DefaultValue != null)
  440. {
  441. if (property != null) property.SetValue(instance, pair.Argument.DefaultValue);
  442. else methodInfo.Invoke(instance, attribute.ManualBinding
  443. ? new[] { pair.Argument.DefaultValue, new BindingEventArgs(pair, attribute) }
  444. : new[] { pair.Argument.DefaultValue });
  445. }
  446. }
  447. }
  448. }
  449.  
  450. private static void BindFlag(Object instance, FlagPair flagPair, PropertyDescriptor property, MethodInfo methodInfo, IOptionAttribute attribute, ParameterPair pair)
  451. {
  452. bool defaultValue = (flagPair.Argument.DefaultValue as Boolean? ?? false);
  453.  
  454. // We're binding a property
  455. if (property != null)
  456. {
  457. if (property.PropertyType == typeof (Boolean))
  458. property.SetValue(instance, flagPair.Count > 0 ^ defaultValue);
  459. else if (flagPair.Count > 0)
  460. property.SetValue(instance, flagPair.Count);
  461. else
  462. foreach (var v in flagPair.Values)
  463. property.SetValue(instance, v);
  464. }
  465. else // ...or invoking a method
  466. {
  467. if (methodInfo.ReturnType == typeof (Boolean))
  468. {
  469. var value = flagPair.Count > 0 ^ defaultValue;
  470. methodInfo.Invoke(instance, attribute.ManualBinding
  471. ? new Object[] { value, new BindingEventArgs(pair, attribute) }
  472. : new[] { (Object) value });
  473. }
  474. else if (attribute.ManualBinding)
  475. methodInfo.Invoke(instance, new Object[] { flagPair.Count, new BindingEventArgs(pair, attribute) });
  476. else if (flagPair.Count > 0)
  477. methodInfo.Invoke(instance, new[] { (Object) flagPair.Count });
  478. else
  479. foreach (var v in flagPair.Values)
  480. methodInfo.Invoke(instance, new[] { v });
  481. }
  482. }
  483. }
  484. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement