PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 1ms

/src/NUnit/framework/Constraints/ConstraintBuilder.cs

#
C# | 269 lines | 138 code | 29 blank | 102 comment | 10 complexity | 1e7f3fc2b355af63370cc4e1f0fc2eef MD5 | raw file
Possible License(s): GPL-2.0
  1. // ****************************************************************
  2. // Copyright 2007, Charlie Poole
  3. // This is free software licensed under the NUnit license. You may
  4. // obtain a copy of the license at http://nunit.org
  5. // ****************************************************************
  6. using System;
  7. using System.Collections;
  8. #if NET_2_0
  9. using System.Collections.Generic;
  10. #endif
  11. namespace NUnit.Framework.Constraints
  12. {
  13. /// <summary>
  14. /// ConstraintBuilder maintains the stacks that are used in
  15. /// processing a ConstraintExpression. An OperatorStack
  16. /// is used to hold operators that are waiting for their
  17. /// operands to be reognized. a ConstraintStack holds
  18. /// input constraints as well as the results of each
  19. /// operator applied.
  20. /// </summary>
  21. public class ConstraintBuilder
  22. {
  23. #region Nested Operator Stack Class
  24. /// <summary>
  25. /// OperatorStack is a type-safe stack for holding ConstraintOperators
  26. /// </summary>
  27. public class OperatorStack
  28. {
  29. #if NET_2_0
  30. private Stack<ConstraintOperator> stack = new Stack<ConstraintOperator>();
  31. #else
  32. private Stack stack = new Stack();
  33. #endif
  34. /// <summary>
  35. /// Initializes a new instance of the <see cref="T:OperatorStack"/> class.
  36. /// </summary>
  37. /// <param name="builder">The builder.</param>
  38. public OperatorStack(ConstraintBuilder builder)
  39. {
  40. }
  41. /// <summary>
  42. /// Gets a value indicating whether this <see cref="T:OpStack"/> is empty.
  43. /// </summary>
  44. /// <value><c>true</c> if empty; otherwise, <c>false</c>.</value>
  45. public bool Empty
  46. {
  47. get { return stack.Count == 0; }
  48. }
  49. /// <summary>
  50. /// Gets the topmost operator without modifying the stack.
  51. /// </summary>
  52. /// <value>The top.</value>
  53. public ConstraintOperator Top
  54. {
  55. get { return (ConstraintOperator)stack.Peek(); }
  56. }
  57. /// <summary>
  58. /// Pushes the specified operator onto the stack.
  59. /// </summary>
  60. /// <param name="op">The op.</param>
  61. public void Push(ConstraintOperator op)
  62. {
  63. stack.Push(op);
  64. }
  65. /// <summary>
  66. /// Pops the topmost operator from the stack.
  67. /// </summary>
  68. /// <returns></returns>
  69. public ConstraintOperator Pop()
  70. {
  71. return (ConstraintOperator)stack.Pop();
  72. }
  73. }
  74. #endregion
  75. #region Nested Constraint Stack Class
  76. /// <summary>
  77. /// ConstraintStack is a type-safe stack for holding Constraints
  78. /// </summary>
  79. public class ConstraintStack
  80. {
  81. #if NET_2_0
  82. private Stack<Constraint> stack = new Stack<Constraint>();
  83. #else
  84. private Stack stack = new Stack();
  85. #endif
  86. private ConstraintBuilder builder;
  87. /// <summary>
  88. /// Initializes a new instance of the <see cref="T:ConstraintStack"/> class.
  89. /// </summary>
  90. /// <param name="builder">The builder.</param>
  91. public ConstraintStack(ConstraintBuilder builder)
  92. {
  93. this.builder = builder;
  94. }
  95. /// <summary>
  96. /// Gets a value indicating whether this <see cref="T:ConstraintStack"/> is empty.
  97. /// </summary>
  98. /// <value><c>true</c> if empty; otherwise, <c>false</c>.</value>
  99. public bool Empty
  100. {
  101. get { return stack.Count == 0; }
  102. }
  103. /// <summary>
  104. /// Gets the topmost constraint without modifying the stack.
  105. /// </summary>
  106. /// <value>The topmost constraint</value>
  107. public Constraint Top
  108. {
  109. get { return (Constraint)stack.Peek(); }
  110. }
  111. /// <summary>
  112. /// Pushes the specified constraint. As a side effect,
  113. /// the constraint's builder field is set to the
  114. /// ConstraintBuilder owning this stack.
  115. /// </summary>
  116. /// <param name="constraint">The constraint.</param>
  117. public void Push(Constraint constraint)
  118. {
  119. stack.Push(constraint);
  120. constraint.SetBuilder( this.builder );
  121. }
  122. /// <summary>
  123. /// Pops this topmost constrait from the stack.
  124. /// As a side effect, the constraint's builder
  125. /// field is set to null.
  126. /// </summary>
  127. /// <returns></returns>
  128. public Constraint Pop()
  129. {
  130. Constraint constraint = (Constraint)stack.Pop();
  131. constraint.SetBuilder( null );
  132. return constraint;
  133. }
  134. }
  135. #endregion
  136. #region Instance Fields
  137. private OperatorStack ops;
  138. private ConstraintStack constraints;
  139. private object lastPushed;
  140. #endregion
  141. #region Constructor
  142. /// <summary>
  143. /// Initializes a new instance of the <see cref="T:ConstraintBuilder"/> class.
  144. /// </summary>
  145. public ConstraintBuilder()
  146. {
  147. this.ops = new OperatorStack(this);
  148. this.constraints = new ConstraintStack(this);
  149. }
  150. #endregion
  151. #region Properties
  152. /// <summary>
  153. /// Gets a value indicating whether this instance is resolvable.
  154. /// </summary>
  155. /// <value>
  156. /// <c>true</c> if this instance is resolvable; otherwise, <c>false</c>.
  157. /// </value>
  158. public bool IsResolvable
  159. {
  160. get { return lastPushed is Constraint || lastPushed is SelfResolvingOperator; }
  161. }
  162. #endregion
  163. #region Public Methods
  164. /// <summary>
  165. /// Appends the specified operator to the expression by first
  166. /// reducing the operator stack and then pushing the new
  167. /// operator on the stack.
  168. /// </summary>
  169. /// <param name="op">The operator to push.</param>
  170. public void Append(ConstraintOperator op)
  171. {
  172. op.LeftContext = lastPushed;
  173. if (lastPushed is ConstraintOperator)
  174. SetTopOperatorRightContext(op);
  175. // Reduce any lower precedence operators
  176. ReduceOperatorStack(op.LeftPrecedence);
  177. ops.Push(op);
  178. lastPushed = op;
  179. }
  180. /// <summary>
  181. /// Appends the specified constraint to the expresson by pushing
  182. /// it on the constraint stack.
  183. /// </summary>
  184. /// <param name="constraint">The constraint to push.</param>
  185. public void Append(Constraint constraint)
  186. {
  187. if (lastPushed is ConstraintOperator)
  188. SetTopOperatorRightContext(constraint);
  189. constraints.Push(constraint);
  190. lastPushed = constraint;
  191. constraint.SetBuilder( this );
  192. }
  193. /// <summary>
  194. /// Sets the top operator right context.
  195. /// </summary>
  196. /// <param name="rightContext">The right context.</param>
  197. private void SetTopOperatorRightContext(object rightContext)
  198. {
  199. // Some operators change their precedence based on
  200. // the right context - save current precedence.
  201. int oldPrecedence = ops.Top.LeftPrecedence;
  202. ops.Top.RightContext = rightContext;
  203. // If the precedence increased, we may be able to
  204. // reduce the region of the stack below the operator
  205. if (ops.Top.LeftPrecedence > oldPrecedence)
  206. {
  207. ConstraintOperator changedOp = ops.Pop();
  208. ReduceOperatorStack(changedOp.LeftPrecedence);
  209. ops.Push(changedOp);
  210. }
  211. }
  212. /// <summary>
  213. /// Reduces the operator stack until the topmost item
  214. /// precedence is greater than or equal to the target precedence.
  215. /// </summary>
  216. /// <param name="targetPrecedence">The target precedence.</param>
  217. private void ReduceOperatorStack(int targetPrecedence)
  218. {
  219. while (!ops.Empty && ops.Top.RightPrecedence < targetPrecedence)
  220. ops.Pop().Reduce(constraints);
  221. }
  222. /// <summary>
  223. /// Resolves this instance, returning a Constraint. If the builder
  224. /// is not currently in a resolvable state, an exception is thrown.
  225. /// </summary>
  226. /// <returns>The resolved constraint</returns>
  227. public Constraint Resolve()
  228. {
  229. if (!IsResolvable)
  230. throw new InvalidOperationException("A partial expression may not be resolved");
  231. while (!ops.Empty)
  232. {
  233. ConstraintOperator op = ops.Pop();
  234. op.Reduce(constraints);
  235. }
  236. return constraints.Pop();
  237. }
  238. #endregion
  239. }
  240. }