PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FluentNHibernate/Visitors/SeparateSubclassVisitor.cs

http://github.com/jagregory/fluent-nhibernate
C# | 160 lines | 98 code | 29 blank | 33 comment | 21 complexity | e4bb44570f828c9e21997f1b6886fd92 MD5 | raw file
Possible License(s): BSD-3-Clause, CC-BY-SA-3.0, Apache-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using FluentNHibernate.Mapping.Providers;
  5. using FluentNHibernate.MappingModel.ClassBased;
  6. using FluentNHibernate.Utils;
  7. namespace FluentNHibernate.Visitors
  8. {
  9. public class SeparateSubclassVisitor : DefaultMappingModelVisitor
  10. {
  11. private readonly IIndeterminateSubclassMappingProviderCollection subclassProviders;
  12. public SeparateSubclassVisitor(IIndeterminateSubclassMappingProviderCollection subclassProviders)
  13. {
  14. this.subclassProviders = subclassProviders;
  15. }
  16. public override void ProcessClass(ClassMapping mapping)
  17. {
  18. var subclasses = FindClosestSubclasses(mapping.Type);
  19. foreach (var provider in subclasses)
  20. mapping.AddSubclass(provider.GetSubclassMapping(GetSubclassType(mapping)));
  21. base.ProcessClass(mapping);
  22. }
  23. public override void ProcessSubclass(SubclassMapping mapping)
  24. {
  25. var subclasses = FindClosestSubclasses(mapping.Type);
  26. foreach (var provider in subclasses)
  27. mapping.AddSubclass(provider.GetSubclassMapping(mapping.SubclassType));
  28. base.ProcessSubclass(mapping);
  29. }
  30. private IEnumerable<IIndeterminateSubclassMappingProvider> FindClosestSubclasses(Type type)
  31. {
  32. var extendsSubclasses = subclassProviders
  33. .Where(x => x.Extends == type);
  34. var subclasses = SortByDistanceFrom(type, subclassProviders.Except(extendsSubclasses));
  35. if (subclasses.Keys.Count == 0 && !extendsSubclasses.Any())
  36. return new IIndeterminateSubclassMappingProvider[0];
  37. if (subclasses.Keys.Count == 0)
  38. return extendsSubclasses;
  39. var lowestDistance = subclasses.Keys.Min();
  40. return subclasses[lowestDistance].Concat(extendsSubclasses);
  41. }
  42. private SubclassType GetSubclassType(ClassMapping mapping)
  43. {
  44. if (mapping.IsUnionSubclass)
  45. {
  46. return SubclassType.UnionSubclass;
  47. }
  48. if (mapping.Discriminator == null)
  49. return SubclassType.JoinedSubclass;
  50. return SubclassType.Subclass;
  51. }
  52. private bool IsMapped(Type type, IIndeterminateSubclassMappingProviderCollection providers)
  53. {
  54. return providers.IsTypeMapped(type);
  55. }
  56. /// <summary>
  57. /// Takes a type that represents the level in the class/subclass-hiearchy that we're starting from, the parent,
  58. /// this can be a class or subclass; also takes a list of subclass providers. The providers are then iterated
  59. /// and added to a dictionary key'd by the types "distance" from the parentType; distance being the number of levels
  60. /// between parentType and the subclass-type.
  61. ///
  62. /// By default if the Parent type is an interface the level will always be zero. At this time there is no check for
  63. /// hierarchical interface inheritance.
  64. /// </summary>
  65. /// <param name="parentType">Starting point, parent type.</param>
  66. /// <param name="subProviders">List of subclasses</param>
  67. /// <returns>Dictionary key'd by the distance from the parentType.</returns>
  68. private IDictionary<int, IList<IIndeterminateSubclassMappingProvider>> SortByDistanceFrom(Type parentType, IEnumerable<IIndeterminateSubclassMappingProvider> subProviders)
  69. {
  70. var arranged = new Dictionary<int, IList<IIndeterminateSubclassMappingProvider>>();
  71. foreach (var subclassProvider in subProviders)
  72. {
  73. var subclassType = subclassProvider.EntityType;
  74. var level = 0;
  75. bool implOfParent = (parentType.IsInterface || subclassType.IsInterface)
  76. ? DistanceFromParentInterface(parentType, subclassType, ref level)
  77. : DistanceFromParentBase(parentType, subclassType.BaseType, ref level);
  78. if (!implOfParent) continue;
  79. if (!arranged.ContainsKey(level))
  80. arranged[level] = new List<IIndeterminateSubclassMappingProvider>();
  81. arranged[level].Add(subclassProvider);
  82. }
  83. return arranged;
  84. }
  85. /// <summary>
  86. /// The evalType starts out as the original subclass. The class hiearchy is only
  87. /// walked if the subclass inherits from a class that is included in the subclassProviders.
  88. /// </summary>
  89. /// <param name="parentType"></param>
  90. /// <param name="evalType"></param>
  91. /// <param name="level"></param>
  92. /// <returns></returns>
  93. private bool DistanceFromParentInterface(Type parentType, Type evalType, ref int level)
  94. {
  95. if (!evalType.HasInterface(parentType)) return false;
  96. if (!(evalType == typeof(object)) &&
  97. IsMapped(evalType.BaseType, subclassProviders))
  98. {
  99. //Walk the tree if the subclasses base class is also in the subclassProviders
  100. level++;
  101. DistanceFromParentInterface(parentType, evalType.BaseType, ref level);
  102. }
  103. return true;
  104. }
  105. /// <summary>
  106. /// The evalType is always one class higher in the hiearchy starting from the original subclass. The class
  107. /// hiearchy is walked until the IsTopLevel (base class is Object) is met. The level is only incremented if
  108. /// the subclass inherits from a class that is also in the subclassProviders.
  109. /// </summary>
  110. /// <param name="parentType"></param>
  111. /// <param name="evalType"></param>
  112. /// <param name="level"></param>
  113. /// <returns></returns>
  114. private bool DistanceFromParentBase(Type parentType, Type evalType, ref int level)
  115. {
  116. var evalImplementsParent = false;
  117. if (evalType == parentType)
  118. evalImplementsParent = true;
  119. if (!evalImplementsParent && !(evalType == typeof(object)))
  120. {
  121. //If the eval class does not inherit the parent but it is included
  122. //in the subclassprovides, then the original subclass can not inherit
  123. //directly from the parent.
  124. if (IsMapped(evalType, subclassProviders))
  125. level++;
  126. evalImplementsParent = DistanceFromParentBase(parentType, evalType.BaseType, ref level);
  127. }
  128. return evalImplementsParent;
  129. }
  130. }
  131. }