PageRenderTime 113ms CodeModel.GetById 44ms RepoModel.GetById 0ms app.codeStats 0ms

/src/LinFu.IoC/Configuration/Resolvers/MethodFinder.cs

http://github.com/philiplaureano/LinFu
C# | 208 lines | 119 code | 31 blank | 58 comment | 15 complexity | e09b2aebfa37f65dd27f0c5c4d3e9fd9 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using LinFu.Finders;
  6. using LinFu.Finders.Interfaces;
  7. using LinFu.IoC.Configuration.Interfaces;
  8. using LinFu.IoC.Interfaces;
  9. namespace LinFu.IoC.Configuration
  10. {
  11. /// <summary>
  12. /// Represents a class that determines which method best matches the
  13. /// services currently in the target container.
  14. /// </summary>
  15. /// <typeparam name="T">The method type to search.</typeparam>
  16. public class MethodFinder<T> : IMethodFinder<T>
  17. where T : MethodBase
  18. {
  19. /// <summary>
  20. /// Determines which method best matches the
  21. /// services currently in the target container.
  22. /// </summary>
  23. /// <param name="items">The list of methods to search.</param>
  24. /// <param name="finderContext">The <see cref="IMethodFinderContext" /> that describes the target method.</param>
  25. /// <returns>
  26. /// Returns the method with the most resolvable parameters from the target <see cref="IServiceContainer" />
  27. /// instance.
  28. /// </returns>
  29. public T GetBestMatch(IEnumerable<T> items, IMethodFinderContext finderContext)
  30. {
  31. T bestMatch = null;
  32. var fuzzyList = items.AsFuzzyList();
  33. // Return the first item
  34. // if there is no other alternative
  35. if (fuzzyList.Count == 1)
  36. return fuzzyList[0].Item;
  37. var additionalArguments = finderContext.Arguments;
  38. var additionalArgumentTypes = (from argument in additionalArguments
  39. let argumentType =
  40. argument == null ? typeof(object) : argument.GetType()
  41. select argumentType).ToList();
  42. Rank(fuzzyList, finderContext);
  43. // Match the generic parameter types
  44. var genericParameterCount = finderContext.TypeArguments != null ? finderContext.TypeArguments.Count() : 0;
  45. Func<T, bool> matchGenericArgumentCount = method =>
  46. {
  47. var genericArguments = method.IsGenericMethod
  48. ? method.GetGenericArguments
  49. ()
  50. : new Type[0];
  51. var currentParameterCount = genericArguments.Count();
  52. return genericParameterCount == currentParameterCount;
  53. };
  54. fuzzyList.AddCriteria(matchGenericArgumentCount, CriteriaType.Critical);
  55. var candidates = fuzzyList.Where(fuzzy => fuzzy.Confidence > 0);
  56. bestMatch = SelectBestMatch(candidates);
  57. // If all else fails, find the method
  58. // that matches only the additional arguments
  59. if (bestMatch != null)
  60. return bestMatch;
  61. return GetNextBestMatch(fuzzyList, additionalArgumentTypes, bestMatch);
  62. }
  63. private T GetNextBestMatch(IList<IFuzzyItem<T>> fuzzyList, List<Type> additionalArgumentTypes, T bestMatch)
  64. {
  65. var additionalArgumentCount = additionalArgumentTypes.Count;
  66. fuzzyList.Reset();
  67. // Match the number of arguments
  68. Func<T, bool> matchParameterCount = method =>
  69. {
  70. var parameters = method.GetParameters();
  71. var parameterCount = parameters != null
  72. ? parameters.Length
  73. : 0;
  74. return parameterCount == additionalArgumentCount;
  75. };
  76. // Remove any methods that do not match
  77. // the parameter count
  78. fuzzyList.AddCriteria(matchParameterCount, CriteriaType.Critical);
  79. CheckArguments(fuzzyList, additionalArgumentTypes);
  80. var nextBestMatch = fuzzyList.BestMatch();
  81. if (nextBestMatch == null)
  82. return null;
  83. return nextBestMatch.Item;
  84. }
  85. /// <summary>
  86. /// Determines which item among the <paramref name="candidates" /> is the best match.
  87. /// </summary>
  88. /// <param name="candidates">The list of possible matches.</param>
  89. /// <returns>The best match if found; otherwise, it should return <c>null</c>.</returns>
  90. protected virtual T SelectBestMatch(IEnumerable<IFuzzyItem<T>> candidates)
  91. {
  92. var bestMatch = default(T);
  93. // Since the remaining constructors all have
  94. // parameter types that currently exist
  95. // in the container as a service,
  96. // the best match will be the constructor with
  97. // the most parameters
  98. var bestParameterCount = -1;
  99. foreach (var candidate in candidates)
  100. {
  101. var currentItem = candidate.Item;
  102. var parameters = currentItem.GetParameters();
  103. var parameterCount = parameters.Count();
  104. if (parameterCount <= bestParameterCount)
  105. continue;
  106. bestMatch = currentItem;
  107. bestParameterCount = parameterCount;
  108. }
  109. return bestMatch;
  110. }
  111. /// <summary>
  112. /// Adds additional <see cref="ICriteria{T}" /> to the fuzzy search list.
  113. /// </summary>
  114. /// <param name="methods">The list of methods to rank.</param>
  115. /// <param name="finderContext">The <see cref="IMethodFinderContext" /> that describes the target method.</param>
  116. protected virtual void Rank(IList<IFuzzyItem<T>> methods, IMethodFinderContext finderContext)
  117. {
  118. }
  119. /// <summary>
  120. /// Attempts to match the <paramref name="additionalArgumentTypes" /> against the
  121. /// <paramref name="fuzzyList">list of methods</paramref>.
  122. /// </summary>
  123. /// <param name="fuzzyList">The list of items currently being compared.</param>
  124. /// <param name="additionalArgumentTypes">
  125. /// The set of <see cref="Type" /> instances that describe each supplied argument
  126. /// type.
  127. /// </param>
  128. private static void CheckArguments(IList<IFuzzyItem<T>> fuzzyList,
  129. IEnumerable<Type> additionalArgumentTypes)
  130. {
  131. var argTypes = additionalArgumentTypes.ToArray();
  132. for (var i = 1; i <= argTypes.Length; i++)
  133. {
  134. var currentOffset = i;
  135. var currentIndex = argTypes.Length - currentOffset;
  136. var currentArgumentType = argTypes[currentIndex];
  137. Func<T, bool> hasCompatibleArgument = method =>
  138. {
  139. var parameters = method.GetParameters();
  140. var parameterCount = parameters.Length;
  141. var targetParameterIndex = currentIndex;
  142. // Make sure that the index is valid
  143. if (targetParameterIndex < 0 ||
  144. targetParameterIndex >= parameterCount)
  145. return false;
  146. // The parameter type must be compatible with the
  147. // given argument type
  148. var parameterType =
  149. parameters[targetParameterIndex].ParameterType;
  150. return parameterType.IsAssignableFrom(currentArgumentType);
  151. };
  152. // Match each additional argument type to its
  153. // relative position from the end of the parameter
  154. // list
  155. fuzzyList.AddCriteria(hasCompatibleArgument, CriteriaType.Critical);
  156. Func<T, bool> hasExactArgumentType = method =>
  157. {
  158. var parameters = method.GetParameters();
  159. var parameterCount = parameters.Length;
  160. var targetParameterIndex = currentIndex;
  161. // Make sure that the index is valid
  162. if (targetParameterIndex < 0 ||
  163. targetParameterIndex >= parameterCount)
  164. return false;
  165. // The parameter type should match the
  166. // given argument type
  167. var parameterType =
  168. parameters[targetParameterIndex].ParameterType;
  169. return parameterType.IsAssignableFrom(currentArgumentType);
  170. };
  171. // Make sure that the finder prefers exact
  172. // type matches over compatible types
  173. fuzzyList.AddCriteria(hasExactArgumentType, CriteriaType.Optional);
  174. }
  175. }
  176. }
  177. }