/src/FubuMVC.Core/Registration/Nodes/BehaviorChain.cs

https://github.com/hartez/fubumvc · C# · 318 lines · 173 code · 57 blank · 88 comment · 23 complexity · 3c866dc434a31573bd0a0b0f833b671e MD5 · raw file

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using FubuCore;
  8. using FubuMVC.Core.Behaviors;
  9. using FubuMVC.Core.Diagnostics.Tracing;
  10. using FubuMVC.Core.Registration.ObjectGraph;
  11. using FubuMVC.Core.Registration.Routes;
  12. using FubuMVC.Core.Security;
  13. namespace FubuMVC.Core.Registration.Nodes
  14. {
  15. /// <summary>
  16. /// BehaviorChain is a configuration model for a single endpoint in a
  17. /// FubuMVC system. Models route information, the behaviors, and
  18. /// authorization rules
  19. /// system
  20. /// </summary>
  21. public class BehaviorChain : IRegisterable, IContainerModel, IEnumerable<BehaviorNode>
  22. {
  23. private BehaviorNode _top;
  24. public BehaviorChain()
  25. {
  26. Authorization = new AuthorizationNode();
  27. UrlCategory = new UrlCategory();
  28. }
  29. public Guid UniqueId
  30. {
  31. get { return Top == null ? Guid.Empty : Top.UniqueId; }
  32. }
  33. /// <summary>
  34. /// The outermost BehaviorNode in the chain
  35. /// </summary>
  36. public BehaviorNode Top
  37. {
  38. get { return _top; }
  39. }
  40. /// <summary>
  41. /// Marks what package or FubuRegistry created this BehaviorChain
  42. /// for the sake of diagnostics
  43. /// </summary>
  44. public string Origin { get; set; }
  45. /// <summary>
  46. /// All the ActionCall nodes in this chain
  47. /// </summary>
  48. public IEnumerable<ActionCall> Calls
  49. {
  50. get { return this.OfType<ActionCall>(); }
  51. }
  52. /// <summary>
  53. /// All the Output nodes in this chain
  54. /// </summary>
  55. public IEnumerable<BehaviorNode> Outputs
  56. {
  57. get { return this.Where(x => x.Category == BehaviorCategory.Output); }
  58. }
  59. /// <summary>
  60. /// Marking a BehaviorChain as "PartialOnly" means that no
  61. /// Route will be generated and registered for this BehaviorChain.
  62. /// Set this property to true if you only want this BehaviorChain
  63. /// to apply to partial requests.
  64. /// </summary>
  65. public bool IsPartialOnly { get; set; }
  66. /// <summary>
  67. /// Models how the Route for this BehaviorChain will be generated
  68. /// </summary>
  69. public IRouteDefinition Route { get; set; }
  70. /// <summary>
  71. /// Categorizes this BehaviorChain for the IUrlRegistry and
  72. /// IEndpointService UrlFor(***, category) methods
  73. /// </summary>
  74. public UrlCategory UrlCategory { get; private set; }
  75. /// <summary>
  76. /// Model of the authorization rules for this BehaviorChain
  77. /// </summary>
  78. public AuthorizationNode Authorization { get; private set; }
  79. public int Rank
  80. {
  81. get
  82. {
  83. return IsPartialOnly || Route == null ? 0 : Route.Rank;
  84. }
  85. }
  86. public IEnumerator<BehaviorNode> GetEnumerator()
  87. {
  88. if (Top == null) yield break;
  89. yield return Top;
  90. foreach (var node in Top)
  91. {
  92. yield return node;
  93. }
  94. }
  95. IEnumerator IEnumerable.GetEnumerator()
  96. {
  97. return GetEnumerator();
  98. }
  99. internal void SetTop(BehaviorNode node)
  100. {
  101. node.Previous = null;
  102. if (_top != null)
  103. {
  104. _top.Chain = null;
  105. }
  106. _top = node;
  107. node.Chain = this;
  108. }
  109. /// <summary>
  110. /// Tests whether or not this chain has any output nodes
  111. /// </summary>
  112. /// <returns></returns>
  113. public bool HasOutputBehavior()
  114. {
  115. return Top == null ? false : Top.HasAnyOutputBehavior();
  116. }
  117. /// <summary>
  118. /// Prepends the prefix to the route definition
  119. /// </summary>
  120. /// <param name="prefix"></param>
  121. public void PrependToUrl(string prefix)
  122. {
  123. if (Route != null)
  124. {
  125. Route.Prepend(prefix);
  126. }
  127. }
  128. /// <summary>
  129. /// Adds a new BehaviorNode to the very end of this behavior chain
  130. /// </summary>
  131. /// <param name="node"></param>
  132. public void AddToEnd(BehaviorNode node)
  133. {
  134. if (Top == null)
  135. {
  136. SetTop(node);
  137. return;
  138. }
  139. Top.AddToEnd(node);
  140. }
  141. /// <summary>
  142. /// Adds a new BehaviorNode of type T to the very end of this
  143. /// behavior chain
  144. /// </summary>
  145. /// <typeparam name="T"></typeparam>
  146. /// <returns></returns>
  147. public T AddToEnd<T>() where T : BehaviorNode, new()
  148. {
  149. var node = new T();
  150. AddToEnd(node);
  151. return node;
  152. }
  153. /// <summary>
  154. /// Finds the output model type of the *last*
  155. /// ActionCall in this BehaviorChain. May be null
  156. /// </summary>
  157. /// <returns></returns>
  158. public Type ActionOutputType()
  159. {
  160. var call = Calls.LastOrDefault();
  161. return call == null ? null : call.OutputType();
  162. }
  163. ObjectDef IContainerModel.ToObjectDef(DiagnosticLevel diagnosticLevel)
  164. {
  165. return buildObjectDef(diagnosticLevel);
  166. }
  167. void IRegisterable.Register(DiagnosticLevel diagnosticLevel, Action<Type, ObjectDef> callback)
  168. {
  169. ObjectDef objectDef = buildObjectDef(diagnosticLevel);
  170. callback(typeof (IActionBehavior), objectDef);
  171. Authorization.As<IAuthorizationRegistration>().Register(Top.UniqueId, callback);
  172. }
  173. private ObjectDef buildObjectDef(DiagnosticLevel diagnosticLevel)
  174. {
  175. var topDef = Top.As<IContainerModel>().ToObjectDef(diagnosticLevel);
  176. if (diagnosticLevel == DiagnosticLevel.FullRequestTracing && !IsPartialOnly)
  177. {
  178. var objectDef = new ObjectDef(typeof (DiagnosticBehavior)){
  179. Name = UniqueId.ToString()
  180. };
  181. objectDef.DependencyByType<IActionBehavior>(topDef);
  182. topDef.Name = Guid.NewGuid().ToString();
  183. var list = new List<ObjectDef>();
  184. var def = topDef;
  185. while (def != null)
  186. {
  187. list.Add(def);
  188. def = def.FindDependencyDefinitionFor<IActionBehavior>();
  189. }
  190. Debug.WriteLine("CHAIN");
  191. list.Each(x => Debug.WriteLine(x.Name));
  192. Debug.WriteLine("-----------------------------------------------------------------");
  193. return objectDef;
  194. }
  195. return topDef;
  196. }
  197. /// <summary>
  198. /// Sets the specified BehaviorNode as the outermost node
  199. /// in this chain
  200. /// </summary>
  201. /// <param name="node"></param>
  202. public void Prepend(BehaviorNode node)
  203. {
  204. var next = Top;
  205. SetTop(node);
  206. if (next != null)
  207. {
  208. Top.Next = next;
  209. }
  210. }
  211. /// <summary>
  212. /// The first ActionCall in this BehaviorChain. Can be null.
  213. /// </summary>
  214. /// <returns></returns>
  215. public ActionCall FirstCall()
  216. {
  217. return Calls.FirstOrDefault();
  218. }
  219. /// <summary>
  220. /// Returns the *last* ActionCall in this
  221. /// BehaviorChain. May be null.
  222. /// </summary>
  223. /// <returns></returns>
  224. public ActionCall LastCall()
  225. {
  226. return Calls.LastOrDefault();
  227. }
  228. /// <summary>
  229. /// Returns the InputType of the very first
  230. /// </summary>
  231. /// <returns></returns>
  232. public Type InputType()
  233. {
  234. var inputTypeHolder = this.OfType<IMayHaveInputType>().FirstOrDefault();
  235. return inputTypeHolder == null ? null : inputTypeHolder.InputType();
  236. }
  237. /// <summary>
  238. /// Creates a new BehaviorChain for an action method
  239. /// </summary>
  240. /// <typeparam name="T"></typeparam>
  241. /// <param name="expression"></param>
  242. /// <returns></returns>
  243. public static BehaviorChain For<T>(Expression<Action<T>> expression)
  244. {
  245. var call = ActionCall.For(expression);
  246. var chain = new BehaviorChain();
  247. chain.AddToEnd(call);
  248. return chain;
  249. }
  250. /// <summary>
  251. /// Checks to see if a Wrapper node of the requested behaviorType anywhere in the chain
  252. /// regardless of position
  253. /// </summary>
  254. /// <param name="behaviorType"></param>
  255. /// <returns></returns>
  256. public bool IsWrappedBy(Type behaviorType)
  257. {
  258. return this.Where(x => x is Wrapper).Cast<Wrapper>().Any(x => x.BehaviorType == behaviorType);
  259. }
  260. }
  261. }