/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}