/IdeIntegration/Vs2010Integration/LanguageService/VsStepSuggestionBindingCollector.cs

https://github.com/danemorgridge/SpecFlow · C# · 247 lines · 216 code · 31 blank · 0 comment · 31 complexity · 6c5a070a2c6732dba7f79ca0cf394e67 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text.RegularExpressions;
  5. using EnvDTE;
  6. using EnvDTE80;
  7. using TechTalk.SpecFlow.Bindings;
  8. using TechTalk.SpecFlow.Bindings.Reflection;
  9. using TechTalk.SpecFlow.Vs2010Integration.Utils;
  10. namespace TechTalk.SpecFlow.Vs2010Integration.LanguageService
  11. {
  12. internal class VsStepSuggestionBindingCollector
  13. {
  14. public IEnumerable<StepBindingNew> GetBindingsFromProjectItem(ProjectItem projectItem)
  15. {
  16. foreach (CodeClass bindingClassWithBindingAttribute in VsxHelper.GetClasses(projectItem).Where(IsBindingClass))
  17. {
  18. CodeClass2 bindingClassIncludingParts = bindingClassWithBindingAttribute as CodeClass2;
  19. BindingScopeNew[] bindingScopes = GetClassScopes(bindingClassWithBindingAttribute);
  20. foreach (StepBindingNew currrentFoundStep in GetStepsFromClass(bindingClassWithBindingAttribute, bindingScopes))
  21. {
  22. yield return currrentFoundStep;
  23. }
  24. foreach (CodeClass2 currentBindingPartialClass in bindingClassIncludingParts.Parts)
  25. {
  26. foreach (StepBindingNew currentPartialClassStep in GetStepsFromClass(currentBindingPartialClass as CodeClass, bindingScopes))
  27. {
  28. yield return currentPartialClassStep;
  29. }
  30. }
  31. }
  32. }
  33. private BindingScopeNew[] GetClassScopes(CodeClass codeClass)
  34. {
  35. return codeClass.Attributes.Cast<CodeAttribute2>().Select(GetBingingScopeFromAttribute).Where(s => s != null).ToArray();
  36. }
  37. private IEnumerable<StepBindingNew> GetStepsFromClass(CodeClass codeClass, BindingScopeNew[] classScopes)
  38. {
  39. return codeClass.Children.OfType<CodeFunction>().SelectMany(codeFunction => GetSuggestionsFromCodeFunction(codeFunction, classScopes));
  40. }
  41. static public bool IsBindingClass(CodeClass codeClass)
  42. {
  43. try
  44. {
  45. return codeClass.Attributes.Cast<CodeAttribute>().Any(attr => typeof(BindingAttribute).FullName.Equals(attr.FullName));
  46. }
  47. catch(Exception)
  48. {
  49. return false;
  50. }
  51. }
  52. private IEnumerable<StepBindingNew> GetSuggestionsFromCodeFunction(CodeFunction codeFunction, IEnumerable<BindingScopeNew> classBindingScopes)
  53. {
  54. var bindingScopes = classBindingScopes.Concat(codeFunction.Attributes.Cast<CodeAttribute2>().Select(GetBingingScopeFromAttribute).Where(s => s != null)).ToArray();
  55. if (bindingScopes.Any())
  56. {
  57. foreach (var bindingScope in bindingScopes)
  58. {
  59. foreach (var stepBinding in GetSuggestionsFromCodeFunctionForScope(codeFunction, bindingScope))
  60. {
  61. yield return stepBinding;
  62. }
  63. }
  64. }
  65. else
  66. {
  67. foreach (var stepBinding in GetSuggestionsFromCodeFunctionForScope(codeFunction, null))
  68. {
  69. yield return stepBinding;
  70. }
  71. }
  72. }
  73. private IEnumerable<StepBindingNew> GetSuggestionsFromCodeFunctionForScope(CodeFunction codeFunction, BindingScopeNew bindingScope)
  74. {
  75. return codeFunction.Attributes.Cast<CodeAttribute2>()
  76. .SelectMany(codeAttribute => GetStepDefinitionsFromAttribute(codeAttribute, codeFunction, bindingScope))
  77. .Where(binding => binding != null);
  78. }
  79. private string GetStringArgumentValue(CodeAttribute2 codeAttribute, string argumentName)
  80. {
  81. var arg = codeAttribute.Arguments.Cast<CodeAttributeArgument>().FirstOrDefault(a => a.Name == argumentName);
  82. if (arg == null)
  83. return null;
  84. return VsxHelper.ParseCodeStringValue(arg.Value, arg.Language);
  85. }
  86. private BindingScopeNew GetBingingScopeFromAttribute(CodeAttribute2 codeAttribute)
  87. {
  88. try
  89. {
  90. if (IsScopeAttribute(codeAttribute))
  91. {
  92. var tag = GetStringArgumentValue(codeAttribute, "Tag");
  93. string feature = GetStringArgumentValue(codeAttribute, "Feature");
  94. string scenario = GetStringArgumentValue(codeAttribute, "Scenario");
  95. if (tag == null && feature == null && scenario == null)
  96. return null;
  97. return new BindingScopeNew(tag, feature, scenario);
  98. }
  99. return null;
  100. }
  101. catch (Exception)
  102. {
  103. return null;
  104. }
  105. }
  106. private bool IsScopeAttribute(CodeAttribute2 codeAttribute)
  107. {
  108. return
  109. codeAttribute.FullName.Equals(typeof(ScopeAttribute).FullName) ||
  110. #pragma warning disable 612,618
  111. codeAttribute.FullName.Equals(typeof(StepScopeAttribute).FullName);
  112. #pragma warning restore 612,618
  113. }
  114. private IEnumerable<StepBindingNew> GetStepDefinitionsFromAttribute(CodeAttribute2 codeAttribute, CodeFunction codeFunction, BindingScopeNew bindingScope)
  115. {
  116. var normalStepDefinition =
  117. GetBingingFromAttribute(codeAttribute, codeFunction, BindingType.Given, bindingScope) ??
  118. GetBingingFromAttribute(codeAttribute, codeFunction, BindingType.When, bindingScope) ??
  119. GetBingingFromAttribute(codeAttribute, codeFunction, BindingType.Then, bindingScope);
  120. if (normalStepDefinition != null)
  121. {
  122. yield return normalStepDefinition;
  123. yield break;
  124. }
  125. if (IsGeneralStepDefinition(codeAttribute))
  126. {
  127. yield return CreateStepBinding(codeAttribute, codeFunction, BindingType.Given, bindingScope);
  128. yield return CreateStepBinding(codeAttribute, codeFunction, BindingType.When, bindingScope);
  129. yield return CreateStepBinding(codeAttribute, codeFunction, BindingType.Then, bindingScope);
  130. }
  131. }
  132. private StepBindingNew GetBingingFromAttribute(CodeAttribute2 codeAttribute, CodeFunction codeFunction, BindingType bindingType, BindingScopeNew bindingScope)
  133. {
  134. try
  135. {
  136. if (codeAttribute.FullName.Equals(string.Format("TechTalk.SpecFlow.{0}Attribute", bindingType)))
  137. return CreateStepBinding(codeAttribute, codeFunction, bindingType, bindingScope);
  138. return null;
  139. }
  140. catch(Exception)
  141. {
  142. return null;
  143. }
  144. }
  145. private bool IsGeneralStepDefinition(CodeAttribute2 codeAttribute)
  146. {
  147. try
  148. {
  149. return codeAttribute.FullName.Equals(typeof (StepDefinitionAttribute).FullName);
  150. }
  151. catch(Exception)
  152. {
  153. return false;
  154. }
  155. }
  156. private StepBindingNew CreateStepBinding(CodeAttribute2 attr, CodeFunction codeFunction, BindingType bindingType, BindingScopeNew bindingScope)
  157. {
  158. try
  159. {
  160. IBindingMethod bindingMethod = new VsBindingMethod(codeFunction);
  161. var regexArg = attr.Arguments.Cast<CodeAttributeArgument>().FirstOrDefault();
  162. if (regexArg == null)
  163. return null;
  164. var regexString = VsxHelper.ParseCodeStringValue(regexArg.Value, regexArg.Language);
  165. var regex = new Regex("^" + regexString + "$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
  166. return new StepBindingNew(bindingMethod, bindingType, regex, bindingScope);
  167. }
  168. catch(Exception)
  169. {
  170. return null;
  171. }
  172. }
  173. public CodeFunction FindCodeFunction(VsProjectScope projectScope, IBindingMethod bindingMethod)
  174. {
  175. var project = projectScope.Project;
  176. var function = FindCodeFunction(project, bindingMethod);
  177. if (function != null)
  178. return function;
  179. var specFlowProject = projectScope.SpecFlowProjectConfiguration;
  180. if (specFlowProject != null)
  181. {
  182. foreach (var assemblyName in specFlowProject.RuntimeConfiguration.AdditionalStepAssemblies)
  183. {
  184. string simpleName = assemblyName.Split(new[] { ',' }, 2)[0];
  185. var stepProject = VsxHelper.FindProjectByAssemblyName(project.DTE, simpleName);
  186. if (stepProject != null)
  187. {
  188. function = FindCodeFunction(stepProject, bindingMethod);
  189. if (function != null)
  190. return function;
  191. }
  192. }
  193. }
  194. return null;
  195. }
  196. private CodeFunction FindCodeFunction(Project project, IBindingMethod bindingMethod)
  197. {
  198. return GetBindingClassesIncludingPartialClasses(project)
  199. .Where(c => c.FullName == bindingMethod.Type.FullName)
  200. .SelectMany(c => c.GetFunctions()).FirstOrDefault(
  201. f => f.Name == bindingMethod.Name && BindingReflectionExtensions.MethodEquals(bindingMethod, new VsBindingMethod(f)));
  202. }
  203. private IEnumerable<CodeClass> GetBindingClassesIncludingPartialClasses(Project project)
  204. {
  205. foreach (CodeClass bindingClassWithBindingAttribute in VsxHelper.GetClasses(project).Where(IsBindingClass))
  206. {
  207. yield return bindingClassWithBindingAttribute;
  208. CodeClass2 bindingClassIncludingParts = bindingClassWithBindingAttribute as CodeClass2;
  209. foreach (CodeClass2 currentBindingPartialClass in bindingClassIncludingParts.Parts)
  210. {
  211. yield return currentBindingPartialClass as CodeClass;
  212. }
  213. }
  214. }
  215. }
  216. }