1 // ***********************************************************************
2 // Copyright (c) 2007 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
30 using System.Collections.Generic;
32 namespace NUnit.Framework.Constraints
35 /// ConstraintBuilder maintains the stacks that are used in
36 /// processing a ConstraintExpression. An OperatorStack
37 /// is used to hold operators that are waiting for their
38 /// operands to be reorganized. a ConstraintStack holds
39 /// input constraints as well as the results of each
42 public class ConstraintBuilder : IResolveConstraint
44 #region Nested Operator Stack Class
47 /// OperatorStack is a type-safe stack for holding ConstraintOperators
49 public class OperatorStack
51 private readonly Stack<ConstraintOperator> stack = new Stack<ConstraintOperator>();
54 /// Initializes a new instance of the <see cref="OperatorStack"/> class.
56 /// <param name="builder">The ConstraintBuilder using this stack.</param>
57 public OperatorStack(ConstraintBuilder builder)
62 /// Gets a value indicating whether this <see cref="OperatorStack"/> is empty.
64 /// <value><c>true</c> if empty; otherwise, <c>false</c>.</value>
67 get { return stack.Count == 0; }
71 /// Gets the topmost operator without modifying the stack.
73 public ConstraintOperator Top
75 get { return stack.Peek(); }
79 /// Pushes the specified operator onto the stack.
81 /// <param name="op">The operator to put onto the stack.</param>
82 public void Push(ConstraintOperator op)
88 /// Pops the topmost operator from the stack.
90 /// <returns>The topmost operator on the stack</returns>
91 public ConstraintOperator Pop()
99 #region Nested Constraint Stack Class
102 /// ConstraintStack is a type-safe stack for holding Constraints
104 public class ConstraintStack
106 private readonly Stack<IConstraint> stack = new Stack<IConstraint>();
107 private readonly ConstraintBuilder builder;
110 /// Initializes a new instance of the <see cref="ConstraintStack"/> class.
112 /// <param name="builder">The ConstraintBuilder using this stack.</param>
113 public ConstraintStack(ConstraintBuilder builder)
115 this.builder = builder;
119 /// Gets a value indicating whether this <see cref="ConstraintStack"/> is empty.
121 /// <value><c>true</c> if empty; otherwise, <c>false</c>.</value>
124 get { return stack.Count == 0; }
128 /// Pushes the specified constraint. As a side effect,
129 /// the constraint's Builder field is set to the
130 /// ConstraintBuilder owning this stack.
132 /// <param name="constraint">The constraint to put onto the stack</param>
133 public void Push(IConstraint constraint)
135 stack.Push(constraint);
136 constraint.Builder = this.builder;
140 /// Pops this topmost constraint from the stack.
141 /// As a side effect, the constraint's Builder
142 /// field is set to null.
144 /// <returns>The topmost contraint on the stack</returns>
145 public IConstraint Pop()
147 IConstraint constraint = stack.Pop();
148 constraint.Builder = null;
155 #region Instance Fields
157 private readonly OperatorStack ops;
159 private readonly ConstraintStack constraints;
161 private object lastPushed;
168 /// Initializes a new instance of the <see cref="ConstraintBuilder"/> class.
170 public ConstraintBuilder()
172 this.ops = new OperatorStack(this);
173 this.constraints = new ConstraintStack(this);
178 #region Public Methods
181 /// Appends the specified operator to the expression by first
182 /// reducing the operator stack and then pushing the new
183 /// operator on the stack.
185 /// <param name="op">The operator to push.</param>
186 public void Append(ConstraintOperator op)
188 op.LeftContext = lastPushed;
189 if (lastPushed is ConstraintOperator)
190 SetTopOperatorRightContext(op);
192 // Reduce any lower precedence operators
193 ReduceOperatorStack(op.LeftPrecedence);
200 /// Appends the specified constraint to the expression by pushing
201 /// it on the constraint stack.
203 /// <param name="constraint">The constraint to push.</param>
204 public void Append(Constraint constraint)
206 if (lastPushed is ConstraintOperator)
207 SetTopOperatorRightContext(constraint);
209 constraints.Push(constraint);
210 lastPushed = constraint;
211 constraint.Builder = this;
215 /// Sets the top operator right context.
217 /// <param name="rightContext">The right context.</param>
218 private void SetTopOperatorRightContext(object rightContext)
220 // Some operators change their precedence based on
221 // the right context - save current precedence.
222 int oldPrecedence = ops.Top.LeftPrecedence;
224 ops.Top.RightContext = rightContext;
226 // If the precedence increased, we may be able to
227 // reduce the region of the stack below the operator
228 if (ops.Top.LeftPrecedence > oldPrecedence)
230 ConstraintOperator changedOp = ops.Pop();
231 ReduceOperatorStack(changedOp.LeftPrecedence);
237 /// Reduces the operator stack until the topmost item
238 /// precedence is greater than or equal to the target precedence.
240 /// <param name="targetPrecedence">The target precedence.</param>
241 private void ReduceOperatorStack(int targetPrecedence)
243 while (!ops.Empty && ops.Top.RightPrecedence < targetPrecedence)
244 ops.Pop().Reduce(constraints);
249 #region IResolveConstraint Implementation
252 /// Resolves this instance, returning a Constraint. If the Builder
253 /// is not currently in a resolvable state, an exception is thrown.
255 /// <returns>The resolved constraint</returns>
256 public IConstraint Resolve()
259 throw new InvalidOperationException("A partial expression may not be resolved");
263 ConstraintOperator op = ops.Pop();
264 op.Reduce(constraints);
267 return constraints.Pop();
272 #region Helper Methods
275 /// Gets a value indicating whether this instance is resolvable.
278 /// <c>true</c> if this instance is resolvable; otherwise, <c>false</c>.
280 private bool IsResolvable
282 get { return lastPushed is Constraint || lastPushed is SelfResolvingOperator; }