PageRenderTime 27ms CodeModel.GetById 19ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

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

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