PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ZebraFlickr/CommandLine/Parser.cs

https://bitbucket.org/garethl/zebraflickr
C# | 221 lines | 158 code | 38 blank | 25 comment | 21 complexity | 0bf3d74881bac502cf85a5396a22a7a5 MD5 | raw file
  1. #region License
  2. // Copyright (c) 2012 Gareth Lennox (garethl@dwakn.com)
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without modification,
  6. // are permitted provided that the following conditions are met:
  7. //
  8. // * Redistributions of source code must retain the above copyright notice,
  9. // this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above copyright notice,
  11. // this list of conditions and the following disclaimer in the documentation
  12. // and/or other materials provided with the distribution.
  13. // * Neither the name of Gareth Lennox nor the names of its
  14. // contributors may be used to endorse or promote products derived from this
  15. // software without specific prior written permission.
  16. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  18. // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  21. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  25. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. #endregion
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Linq;
  31. using System.Reflection;
  32. using ZebraFlickr.CommandLine.Exceptions;
  33. namespace ZebraFlickr.CommandLine
  34. {
  35. public static class Parser
  36. {
  37. private static readonly IDictionary<Type, ArgumentMap[]> _mapCache = new Dictionary<Type, ArgumentMap[]>();
  38. public static void Parse(object obj, string[] arguments)
  39. {
  40. var map = GetMap(obj.GetType()).OrderBy(k => k.Index);
  41. var args = ParseArgs(map, arguments).ToArray();
  42. var maps = map.ToList();
  43. foreach (var argument in args)
  44. {
  45. if (argument.Map != null)
  46. {
  47. argument.Map.SetValue(obj, argument);
  48. maps.Remove(argument.Map);
  49. }
  50. }
  51. foreach (var remainingMap in maps)
  52. {
  53. if (remainingMap.Required)
  54. throw new ValidationException(string.Format("Missing required parameter {0}", remainingMap.LongName));
  55. }
  56. }
  57. private static IEnumerable<Argument> ParseArgs(IEnumerable<ArgumentMap> map, string[] arguments)
  58. {
  59. var bareMap = map.Where(i => i.IsBareOption).ToArray();
  60. var bareIndex = -1;
  61. for (int x = 0; x < arguments.Length; x++)
  62. {
  63. var argument = arguments[x];
  64. if (argument.StartsWith("--")) //long arg
  65. {
  66. argument = argument.Substring(2);
  67. var indexOfEquals = argument.IndexOf('=');
  68. string value = null;
  69. if (indexOfEquals >= 0)
  70. {
  71. value = argument.Substring(indexOfEquals + 1);
  72. argument = argument.Substring(0, indexOfEquals);
  73. }
  74. var am = map.FirstOrDefault(m => m.LongName == argument || m.OtherLongNames.Contains(argument));
  75. yield return new Argument {Index = x, Key = argument, Value = value, Map = am};
  76. }
  77. else if (argument.StartsWith("-")) //short arg
  78. {
  79. argument = argument.Substring(1);
  80. string value = null;
  81. var am = map.FirstOrDefault(m => m.ShortName == argument || m.OtherShortNames.Contains(argument));
  82. if (am != null)
  83. {
  84. if (am.HasValue)
  85. {
  86. value = arguments[x + 1];
  87. x++;
  88. }
  89. yield return new Argument {Index = x, Key = am.LongName, Value = value, Map = am};
  90. }
  91. }
  92. else //command or bare option
  93. {
  94. if (bareIndex >= 0)
  95. {
  96. if (bareIndex < bareMap.Length)
  97. {
  98. var argumentMap = bareMap[bareIndex];
  99. yield return new Argument {Index = x, Key = null, Value = argument, Map = argumentMap};
  100. bareIndex++;
  101. }
  102. }
  103. else
  104. {
  105. bareIndex = 0;
  106. yield return new Argument {Index = x, Key = null, Value = argument, Map = null};
  107. }
  108. }
  109. }
  110. }
  111. public static string GetUsage(Type type)
  112. {
  113. return HelpBuilder.GetUsage(GetMap(type), x => !x.IsGeneral);
  114. }
  115. public static string GetUsage(Type type, Func<DetailsAttribute, bool> filter)
  116. {
  117. return HelpBuilder.GetUsage(GetMap(type), filter);
  118. }
  119. public static string GetBareUsage(Type type)
  120. {
  121. return HelpBuilder.GetBareUsage(GetMap(type));
  122. }
  123. public static string GetBareDetailedUsage(Type type)
  124. {
  125. return HelpBuilder.GetBareDetailedUsage(GetMap(type));
  126. }
  127. public static ArgumentMap[] GetMap(Type type)
  128. {
  129. lock (_mapCache)
  130. {
  131. ArgumentMap[] value;
  132. if (_mapCache.TryGetValue(type, out value))
  133. return value;
  134. }
  135. var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
  136. IList<ArgumentMap> ret = new List<ArgumentMap>(properties.Length);
  137. foreach (var property in properties)
  138. {
  139. var attributes = property.GetCustomAttributes(typeof (DetailsAttribute), true);
  140. if (attributes.Length == 0)
  141. {
  142. continue;
  143. }
  144. foreach (DetailsAttribute detail in attributes)
  145. {
  146. var map = new ArgumentMap
  147. {
  148. Description = detail.Description,
  149. ShortName = detail.ShortName,
  150. LongName = detail.LongName ?? property.Name.ToLowerInvariant(),
  151. Values = detail.Values,
  152. Index = detail.Index,
  153. Property = property,
  154. Decoder = detail.Decoder,
  155. OtherLongNames = detail.OtherLongNames,
  156. OtherShortNames = detail.OtherShortNames,
  157. Required = detail.Required,
  158. IsBareOption = detail.IsBareOption,
  159. Default = detail.Default,
  160. IsGeneral = detail.IsGeneral
  161. };
  162. ret.Add(map);
  163. }
  164. }
  165. var retArray = (from k in ret
  166. orderby string.IsNullOrEmpty(k.LongName) ? k.ShortName : k.LongName
  167. orderby k.IsBareOption ? 1 : 0
  168. select k)
  169. .ToArray();
  170. _mapCache[type] = retArray;
  171. return retArray;
  172. }
  173. #region Nested type: Argument
  174. public class Argument
  175. {
  176. public ArgumentMap Map { get; set; }
  177. public string Key { get; set; }
  178. public string Value { get; set; }
  179. public int Index { get; set; }
  180. }
  181. #endregion
  182. }
  183. }