PageRenderTime 39ms CodeModel.GetById 16ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

/src/NUnit/framework/Constraints/ConstraintOperators.cs

#
C# | 480 lines | 228 code | 45 blank | 207 comment | 9 complexity | c30275040a2c556ee7f8a8a3f8c9b2f4 MD5 | raw file
  1// ****************************************************************
  2// Copyright 2008, 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    #region ConstraintOperator Base Class
 16    /// <summary>
 17    /// The ConstraintOperator class is used internally by a
 18    /// ConstraintBuilder to represent an operator that 
 19    /// modifies or combines constraints. 
 20    /// 
 21    /// Constraint operators use left and right precedence
 22    /// values to determine whether the top operator on the
 23    /// stack should be reduced before pushing a new operator.
 24    /// </summary>
 25    public abstract class ConstraintOperator
 26    {
 27        private object leftContext;
 28        private object rightContext;
 29
 30		/// <summary>
 31		/// The precedence value used when the operator
 32		/// is about to be pushed to the stack.
 33		/// </summary>
 34		protected int left_precedence;
 35        
 36		/// <summary>
 37		/// The precedence value used when the operator
 38		/// is on the top of the stack.
 39		/// </summary>
 40		protected int right_precedence;
 41
 42		/// <summary>
 43		/// The syntax element preceding this operator
 44		/// </summary>
 45        public object LeftContext
 46        {
 47            get { return leftContext; }
 48            set { leftContext = value; }
 49        }
 50
 51		/// <summary>
 52		/// The syntax element folowing this operator
 53		/// </summary>
 54        public object RightContext
 55        {
 56            get { return rightContext; }
 57            set { rightContext = value; }
 58        }
 59
 60		/// <summary>
 61		/// The precedence value used when the operator
 62		/// is about to be pushed to the stack.
 63		/// </summary>
 64		public virtual int LeftPrecedence
 65        {
 66            get { return left_precedence; }
 67        }
 68
 69		/// <summary>
 70		/// The precedence value used when the operator
 71		/// is on the top of the stack.
 72		/// </summary>
 73		public virtual int RightPrecedence
 74        {
 75            get { return right_precedence; }
 76        }
 77
 78		/// <summary>
 79		/// Reduce produces a constraint from the operator and 
 80		/// any arguments. It takes the arguments from the constraint 
 81		/// stack and pushes the resulting constraint on it.
 82		/// </summary>
 83		/// <param name="stack"></param>
 84		public abstract void Reduce(ConstraintBuilder.ConstraintStack stack);
 85    }
 86    #endregion
 87
 88    #region Prefix Operators
 89
 90    #region PrefixOperator
 91    /// <summary>
 92	/// PrefixOperator takes a single constraint and modifies
 93	/// it's action in some way.
 94	/// </summary>
 95    public abstract class PrefixOperator : ConstraintOperator
 96    {
 97		/// <summary>
 98		/// Reduce produces a constraint from the operator and 
 99		/// any arguments. It takes the arguments from the constraint 
100		/// stack and pushes the resulting constraint on it.
101		/// </summary>
102		/// <param name="stack"></param>
103		public override void Reduce(ConstraintBuilder.ConstraintStack stack)
104        {
105            stack.Push(ApplyPrefix(stack.Pop()));
106        }
107
108        /// <summary>
109        /// Returns the constraint created by applying this
110        /// prefix to another constraint.
111        /// </summary>
112        /// <param name="constraint"></param>
113        /// <returns></returns>
114        public abstract Constraint ApplyPrefix(Constraint constraint);
115    }
116    #endregion
117
118    #region NotOperator
119    /// <summary>
120    /// Negates the test of the constraint it wraps.
121    /// </summary>
122    public class NotOperator : PrefixOperator
123    {
124        /// <summary>
125        /// Constructs a new NotOperator
126        /// </summary>
127        public NotOperator()
128        {
129            // Not stacks on anything and only allows other
130            // prefix ops to stack on top of it.
131            this.left_precedence = this.right_precedence = 1;
132        }
133
134        /// <summary>
135        /// Returns a NotConstraint applied to its argument.
136        /// </summary>
137        public override Constraint ApplyPrefix(Constraint constraint)
138        {
139            return new NotConstraint(constraint);
140        }
141    }
142    #endregion
143
144    #region Collection Operators
145    /// <summary>
146    /// Abstract base for operators that indicate how to
147    /// apply a constraint to items in a collection.
148    /// </summary>
149    public abstract class CollectionOperator : PrefixOperator
150    {
151        /// <summary>
152        /// Constructs a CollectionOperator
153        /// </summary>
154        public CollectionOperator()
155        {
156            // Collection Operators stack on everything
157            // and allow all other ops to stack on them
158            this.left_precedence = 1;
159            this.right_precedence = 10;
160        }
161    }
162
163    /// <summary>
164    /// Represents a constraint that succeeds if all the 
165    /// members of a collection match a base constraint.
166    /// </summary>
167    public class AllOperator : CollectionOperator
168    {
169        /// <summary>
170        /// Returns a constraint that will apply the argument
171        /// to the members of a collection, succeeding if
172        /// they all succeed.
173        /// </summary>
174        public override Constraint ApplyPrefix(Constraint constraint)
175        {
176            return new AllItemsConstraint(constraint);
177        }
178    }
179
180    /// <summary>
181    /// Represents a constraint that succeeds if any of the 
182    /// members of a collection match a base constraint.
183    /// </summary>
184    public class SomeOperator : CollectionOperator
185    {
186        /// <summary>
187        /// Returns a constraint that will apply the argument
188        /// to the members of a collection, succeeding if
189        /// any of them succeed.
190        /// </summary>
191        public override Constraint ApplyPrefix(Constraint constraint)
192        {
193            return new SomeItemsConstraint(constraint);
194        }
195    }
196
197    /// <summary>
198    /// Represents a constraint that succeeds if none of the 
199    /// members of a collection match a base constraint.
200    /// </summary>
201    public class NoneOperator : CollectionOperator
202    {
203        /// <summary>
204        /// Returns a constraint that will apply the argument
205        /// to the members of a collection, succeeding if
206        /// none of them succeed.
207        /// </summary>
208        public override Constraint ApplyPrefix(Constraint constraint)
209        {
210            return new NoItemConstraint(constraint);
211        }
212    }
213    #endregion
214
215    #region WithOperator
216    /// <summary>
217    /// Represents a constraint that simply wraps the
218    /// constraint provided as an argument, without any
219    /// further functionality, but which modifes the
220    /// order of evaluation because of its precedence.
221    /// </summary>
222    public class WithOperator : PrefixOperator
223    {
224        /// <summary>
225        /// Constructor for the WithOperator
226        /// </summary>
227        public WithOperator()
228        {
229            this.left_precedence = 1;
230            this.right_precedence = 4;
231        }
232
233        /// <summary>
234        /// Returns a constraint that wraps its argument
235        /// </summary>
236        public override Constraint ApplyPrefix(Constraint constraint)
237        {
238            return constraint;
239        }
240    }
241    #endregion
242
243    #region SelfResolving Operators
244
245    #region SelfResolvingOperator
246    /// <summary>
247    /// Abstract base class for operators that are able to reduce to a 
248    /// constraint whether or not another syntactic element follows.
249    /// </summary>
250    public abstract class SelfResolvingOperator : ConstraintOperator
251    {
252    }
253    #endregion
254
255    #region PropOperator
256    /// <summary>
257    /// Operator used to test for the presence of a named Property
258    /// on an object and optionally apply further tests to the
259    /// value of that property.
260    /// </summary>
261    public class PropOperator : SelfResolvingOperator
262    {
263        private string name;
264
265        /// <summary>
266        /// Gets the name of the property to which the operator applies
267        /// </summary>
268        public string Name
269        {
270            get { return name; }
271        }
272
273        /// <summary>
274        /// Constructs a PropOperator for a particular named property
275        /// </summary>
276        public PropOperator(string name)
277        {
278            this.name = name;
279
280            // Prop stacks on anything and allows only 
281            // prefix operators to stack on it.
282            this.left_precedence = this.right_precedence = 1;
283        }
284
285        /// <summary>
286        /// Reduce produces a constraint from the operator and 
287        /// any arguments. It takes the arguments from the constraint 
288        /// stack and pushes the resulting constraint on it.
289        /// </summary>
290        /// <param name="stack"></param>
291        public override void Reduce(ConstraintBuilder.ConstraintStack stack)
292        {
293            if (RightContext == null || RightContext is BinaryOperator)
294                stack.Push(new PropertyExistsConstraint(name));
295            else
296                stack.Push(new PropertyConstraint(name, stack.Pop()));
297        }
298    }
299    #endregion
300
301    #region AttributeOperator
302    /// <summary>
303    /// Operator that tests for the presence of a particular attribute
304    /// on a type and optionally applies further tests to the attribute.
305    /// </summary>
306    public class AttributeOperator : SelfResolvingOperator
307    {
308        private Type type;
309
310        /// <summary>
311        /// Construct an AttributeOperator for a particular Type
312        /// </summary>
313        /// <param name="type">The Type of attribute tested</param>
314        public AttributeOperator(Type type)
315        {
316            this.type = type;
317
318            // Attribute stacks on anything and allows only 
319            // prefix operators to stack on it.
320            this.left_precedence = this.right_precedence = 1;
321        }
322
323        /// <summary>
324        /// Reduce produces a constraint from the operator and 
325        /// any arguments. It takes the arguments from the constraint 
326        /// stack and pushes the resulting constraint on it.
327        /// </summary>
328        public override void Reduce(ConstraintBuilder.ConstraintStack stack)
329        {
330            if (RightContext == null || RightContext is BinaryOperator)
331                stack.Push(new AttributeExistsConstraint(type));
332            else
333                stack.Push(new AttributeConstraint(type, stack.Pop()));
334        }
335    }
336    #endregion
337
338    #region ThrowsOperator
339    /// <summary>
340    /// Operator that tests that an exception is thrown and
341    /// optionally applies further tests to the exception.
342    /// </summary>
343    public class ThrowsOperator : SelfResolvingOperator
344    {
345        /// <summary>
346        /// Construct a ThrowsOperator
347        /// </summary>
348        public ThrowsOperator()
349        {
350            // ThrowsOperator stacks on everything but
351            // it's always the first item on the stack
352            // anyway. It is evaluated last of all ops.
353            this.left_precedence = 1;
354            this.right_precedence = 100;
355        }
356
357        /// <summary>
358        /// Reduce produces a constraint from the operator and 
359        /// any arguments. It takes the arguments from the constraint 
360        /// stack and pushes the resulting constraint on it.
361        /// </summary>
362        public override void Reduce(ConstraintBuilder.ConstraintStack stack)
363        {
364            if (RightContext == null || RightContext is BinaryOperator)
365                stack.Push(new ThrowsConstraint(null));
366            else
367                stack.Push(new ThrowsConstraint(stack.Pop()));
368        }
369    }
370    #endregion
371
372    #endregion
373
374    #endregion
375
376    #region Binary Operators
377
378    #region BinaryOperator
379    /// <summary>
380    /// Abstract base class for all binary operators
381    /// </summary>
382    public abstract class BinaryOperator : ConstraintOperator
383    {
384        /// <summary>
385        /// Reduce produces a constraint from the operator and 
386        /// any arguments. It takes the arguments from the constraint 
387        /// stack and pushes the resulting constraint on it.
388        /// </summary>
389        /// <param name="stack"></param>
390        public override void Reduce(ConstraintBuilder.ConstraintStack stack)
391        {
392            Constraint right = stack.Pop();
393            Constraint left = stack.Pop();
394            stack.Push(ApplyOperator(left, right));
395        }
396
397        /// <summary>
398        /// Gets the left precedence of the operator
399        /// </summary>
400        public override int LeftPrecedence
401        {
402            get
403            {
404                return RightContext is CollectionOperator
405                    ? base.LeftPrecedence + 10
406                    : base.LeftPrecedence;
407            }
408        }
409
410        /// <summary>
411        /// Gets the right precedence of the operator
412        /// </summary>
413        public override int RightPrecedence
414        {
415            get
416            {
417                return RightContext is CollectionOperator
418                    ? base.RightPrecedence + 10
419                    : base.RightPrecedence;
420            }
421        }
422
423        /// <summary>
424        /// Abstract method that produces a constraint by applying
425        /// the operator to its left and right constraint arguments.
426        /// </summary>
427        public abstract Constraint ApplyOperator(Constraint left, Constraint right);
428    }
429    #endregion
430
431    #region AndOperator
432    /// <summary>
433    /// Operator that requires both it's arguments to succeed
434    /// </summary>
435    public class AndOperator : BinaryOperator
436    {
437        /// <summary>
438        /// Construct an AndOperator
439        /// </summary>
440        public AndOperator()
441        {
442            this.left_precedence = this.right_precedence = 2;
443        }
444
445        /// <summary>
446        /// Apply the operator to produce an AndConstraint
447        /// </summary>
448        public override Constraint ApplyOperator(Constraint left, Constraint right)
449        {
450            return new AndConstraint(left, right);
451        }
452    }
453    #endregion
454
455    #region OrOperator
456    /// <summary>
457    /// Operator that requires at least one of it's arguments to succeed
458    /// </summary>
459    public class OrOperator : BinaryOperator
460    {
461        /// <summary>
462        /// Construct an OrOperator
463        /// </summary>
464        public OrOperator()
465        {
466            this.left_precedence = this.right_precedence = 3;
467        }
468
469        /// <summary>
470        /// Apply the operator to produce an OrConstraint
471        /// </summary>
472        public override Constraint ApplyOperator(Constraint left, Constraint right)
473        {
474            return new OrConstraint(left, right);
475        }
476    }
477    #endregion
478
479    #endregion
480}