Fixing an issue where setting a custom property on a handler will not propagate it...
[castle.git] / Components / General / Validator / Castle.Components.Validator / Validators / RangeValidator.cs
bloba05eb46e44670c1e9388a3cf3b58fb0599d44ca0
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.Components.Validator
17 using System;
18 using System.Collections;
20 /// <summary>
21 /// Specifies the data type the <see cref="RangeValidator"/>
22 /// is dealing with.
23 /// </summary>
24 public enum RangeValidationType
26 /// <summary>
27 /// <see cref="RangeValidator"/> is dealing with a range of integers
28 /// </summary>
29 Integer,
30 /// <summary>
31 /// <see cref="RangeValidator"/> is dealing with a range of decimals
32 /// </summary>
33 Decimal,
34 /// <summary>
35 /// <see cref="RangeValidator"/> is dealing with a range of dates
36 /// </summary>
37 DateTime,
38 /// <summary>
39 /// <see cref="RangeValidator"/> is dealing with a range of strings
40 /// </summary>
41 String
44 /// <summary>
45 /// Ensures that a property's string representation
46 /// is within the desired value limitations.
47 /// </summary>
48 [Serializable]
49 public class RangeValidator : AbstractValidator
51 private object min, max;
52 private RangeValidationType type;
54 /// <summary>
55 /// Initializes an integer-based range validator.
56 /// </summary>
57 /// <param name="min">The minimum value, or <c>int.MinValue</c> if this should not be tested.</param>
58 /// <param name="max">The maximum value, or <c>int.MaxValue</c> if this should not be tested.</param>
59 public RangeValidator(int min, int max)
61 AssertValid(max, min);
63 type = RangeValidationType.Integer;
64 this.min = min;
65 this.max = max;
68 /// <summary>
69 /// Initializes an decimal-based range validator.
70 /// </summary>
71 /// <param name="min">The minimum value, or <c>decimal.MinValue</c> if this should not be tested.</param>
72 /// <param name="max">The maximum value, or <c>decimal.MaxValue</c> if this should not be tested.</param>
73 public RangeValidator(decimal min, decimal max)
75 AssertValid(max, min);
77 type = RangeValidationType.Decimal;
78 this.min = min;
79 this.max = max;
82 /// <summary>
83 /// Initializes a DateTime-based range validator.
84 /// </summary>
85 /// <param name="min">The minimum value, or <c>DateTime.MinValue</c> if this should not be tested.</param>
86 /// <param name="max">The maximum value, or <c>DateTime.MaxValue</c> if this should not be tested.</param>
87 public RangeValidator(DateTime min, DateTime max)
89 AssertValid(max, min);
91 type = RangeValidationType.DateTime;
92 this.min = min;
93 this.max = max;
96 /// <summary>
97 /// Initializes a string-based range validator.
98 /// </summary>
99 /// <param name="min">The minimum value, or <c>String.Empty</c> if this should not be tested.</param>
100 /// <param name="max">The maximum value, or <c>String.Empty</c> if this should not be tested.</param>
101 public RangeValidator(string min, string max)
103 AssertValid(max, min);
105 type = RangeValidationType.String;
106 this.min = min;
107 this.max = max;
110 /// <summary>
111 /// Initializes a range validator of the given type with the given minimum and maximum values.
112 /// </summary>
113 /// <param name="type">The type of range validator.</param>
114 /// <param name="min">The minimum value, or <c>null</c> if this should not be tested.</param>
115 /// <param name="max">The maximum value, or <c>null</c> if this should not be tested.</param>
116 public RangeValidator(RangeValidationType type, object min, object max)
118 this.type = type;
119 this.min = GetMinValue(min);
120 this.max = GetMaxValue(max);
123 /// <summary>
124 /// Gets or sets the range validation type for this validator. If the type is changed,
125 /// the minimum and maximum values are reset to null-equivalent values (i.e. appropriate
126 /// minimum and maximum values for the data type).
127 /// </summary>
128 public RangeValidationType Type
130 get { return type; }
133 if (value != type)
135 type = value;
136 min = GetMinValue(null);
137 max = GetMaxValue(null);
143 /// <summary>
144 /// Internal method that checks a given maximum value's data type and converts
145 /// null values to the proper maximum value for the data type.
146 /// </summary>
147 /// <param name="max">The maximum value to be processed.</param>
148 /// <returns>The maximum value with appropriate null-converted minimum values.</returns>
149 private object GetMaxValue(object max)
153 //check properties for valid types
154 switch(type)
156 case RangeValidationType.Integer:
157 return GetIntValue(max, int.MaxValue);
158 case RangeValidationType.Decimal:
159 return GetDecimalValue(max, decimal.MaxValue);
160 case RangeValidationType.DateTime:
161 return GetDateTimeValue(max, DateTime.MaxValue);
162 case RangeValidationType.String:
163 return (max == null || String.IsNullOrEmpty(max.ToString()) ? String.Empty : max.ToString());
164 default:
165 throw new ArgumentException("Unknown RangeValidatorType found.");
168 catch(InvalidCastException)
170 throw new ArgumentException(
171 "RangeValidator's maximum value data type is incompatible with the RangeValidationType specified.");
175 /// <summary>
176 /// Validate that the property value matches the value requirements.
177 /// </summary>
178 /// <param name="instance"></param>
179 /// <param name="fieldValue"></param>
180 /// <returns><c>true</c> if the field is OK</returns>
181 public override bool IsValid(object instance, object fieldValue)
183 if ((fieldValue == null) || (String.IsNullOrEmpty(fieldValue.ToString())))
185 return true;
188 bool valid = false;
192 switch(type)
194 case RangeValidationType.Integer:
195 int intValue;
198 intValue = (int) fieldValue;
199 valid = intValue >= (int) min && intValue <= (int) max;
201 catch
203 if (int.TryParse(fieldValue.ToString(), out intValue))
205 valid = intValue >= (int) min && intValue <= (int) max;
208 break;
209 case RangeValidationType.Decimal:
210 decimal decimalValue;
213 decimalValue = (decimal) fieldValue;
214 valid = decimalValue >= (decimal) min && decimalValue <= (decimal) max;
216 catch
218 if (decimal.TryParse(fieldValue.ToString(), out decimalValue))
220 valid = decimalValue >= (decimal) min && decimalValue <= (decimal) max;
223 break;
224 case RangeValidationType.DateTime:
225 DateTime dtValue;
228 dtValue = (DateTime) fieldValue;
229 valid = dtValue >= (DateTime) min && dtValue <= (DateTime) max;
231 catch
233 if (DateTime.TryParse(fieldValue.ToString(), out dtValue))
235 valid = dtValue >= (DateTime) min && dtValue <= (DateTime) max;
238 break;
239 case RangeValidationType.String:
240 string stringValue = fieldValue.ToString();
241 string minv = min.ToString();
242 string maxv = max.ToString();
243 valid = (
244 (String.IsNullOrEmpty(minv) ||
245 String.Compare(stringValue, minv, StringComparison.InvariantCultureIgnoreCase) >= 0)
247 (String.IsNullOrEmpty(maxv) ||
248 String.Compare(stringValue, maxv, StringComparison.InvariantCultureIgnoreCase) <= 0)
250 break;
251 default:
252 valid = false;
253 break;
256 catch
260 return valid;
263 /// <summary>
264 /// Gets a value indicating whether this validator supports browser validation.
265 /// </summary>
266 /// <value>
267 /// <see langword="true"/> if browser validation is supported; otherwise, <see langword="false"/>.
268 /// </value>
269 public override bool SupportsBrowserValidation
271 get { return true; }
274 /// <summary>
275 /// Applies the browser validation by setting up one or
276 /// more input rules on <see cref="IBrowserValidationGenerator"/>.
277 /// </summary>
278 /// <param name="config">The config.</param>
279 /// <param name="inputType">Type of the input.</param>
280 /// <param name="generator">The generator.</param>
281 /// <param name="attributes">The attributes.</param>
282 /// <param name="target">The target.</param>
283 public override void ApplyBrowserValidation(BrowserValidationConfiguration config, InputElementType inputType,
284 IBrowserValidationGenerator generator, IDictionary attributes,
285 string target)
287 base.ApplyBrowserValidation(config, inputType, generator, attributes, target);
289 switch(type)
291 case RangeValidationType.Integer:
292 generator.SetValueRange(target, (int) min, (int) max, BuildErrorMessage());
293 break;
294 case RangeValidationType.Decimal:
295 generator.SetValueRange(target, (decimal) min, (decimal) max, BuildErrorMessage());
296 break;
297 case RangeValidationType.DateTime:
298 generator.SetValueRange(target, (DateTime) min, (DateTime) max, BuildErrorMessage());
299 break;
300 case RangeValidationType.String:
301 generator.SetValueRange(target, (string) min, (string) max, BuildErrorMessage());
302 break;
303 default:
304 throw new ArgumentOutOfRangeException();
308 /// <summary>
309 /// Builds the error message.
310 /// </summary>
311 /// <returns></returns>
312 protected override string BuildErrorMessage()
314 if (type == RangeValidationType.DateTime)
316 return BuildDateTimeErrorMessage((DateTime) min, (DateTime) max);
319 if (type == RangeValidationType.String)
321 return BuildStringErrorMessage(min.ToString(), max.ToString());
324 if (type == RangeValidationType.Integer)
326 return BuildIntegerErrorMessage((int) min, (int) max);
329 if (type == RangeValidationType.Decimal)
331 return BuildDecimalErrorMessage((decimal) min, (decimal) max);
334 throw new InvalidOperationException();
337 /// <summary>
338 /// Gets the error message string for Integer validation
339 /// </summary>
340 /// <returns>an error message</returns>
341 protected string BuildIntegerErrorMessage(int min, int max)
343 if (min == int.MinValue && max != int.MaxValue)
345 // range against max value only
346 return string.Format(GetString(MessageConstants.RangeTooHighMessage), max);
348 else if (min != int.MinValue && max == int.MaxValue)
350 // range against min value only
351 return string.Format(GetString(MessageConstants.RangeTooLowMessage), min);
353 else if (min != int.MinValue || max != int.MaxValue)
355 return string.Format(GetString(MessageConstants.RangeTooHighOrLowMessage), min, max);
357 else
359 throw new InvalidOperationException();
363 /// <summary>
364 /// Gets the error message string for Decimal validation
365 /// </summary>
366 /// <returns>an error message</returns>
367 protected string BuildDecimalErrorMessage(decimal min, decimal max)
369 if (min == decimal.MinValue && max != decimal.MaxValue)
371 // range against max value only
372 return string.Format(GetString(MessageConstants.RangeTooHighMessage), max);
374 else if (min != decimal.MinValue && max == decimal.MaxValue)
376 // range against min value only
377 return string.Format(GetString(MessageConstants.RangeTooLowMessage), min);
379 else if (min != decimal.MinValue || max != decimal.MaxValue)
381 return string.Format(GetString(MessageConstants.RangeTooHighOrLowMessage), min, max);
383 else
385 throw new InvalidOperationException();
390 /// <summary>
391 /// Gets the error message string for DateTime validation
392 /// </summary>
393 /// <returns>an error message</returns>
394 protected string BuildDateTimeErrorMessage(DateTime min, DateTime max)
396 if (min == DateTime.MinValue && max != DateTime.MaxValue)
398 // range against max value only
399 return string.Format(GetString(MessageConstants.RangeTooHighMessage), max);
401 else if (min != DateTime.MinValue && max == DateTime.MaxValue)
403 // range against min value only
404 return string.Format(GetString(MessageConstants.RangeTooLowMessage), min);
406 else if (min != DateTime.MinValue || max != DateTime.MaxValue)
408 return string.Format(GetString(MessageConstants.RangeTooHighOrLowMessage), min, max);
410 else
412 throw new InvalidOperationException();
416 /// <summary>
417 /// Gets the error message string for string validation
418 /// </summary>
419 /// <returns>an error message</returns>
420 protected string BuildStringErrorMessage(string min, string max)
422 if (String.IsNullOrEmpty(min) && !String.IsNullOrEmpty(max))
424 // range against max value only
425 return string.Format(GetString(MessageConstants.RangeTooHighMessage), max);
427 else if (!String.IsNullOrEmpty(min) && String.IsNullOrEmpty(max))
429 // range against min value only
430 return string.Format(GetString(MessageConstants.RangeTooLowMessage), min);
432 else if (!String.IsNullOrEmpty(min) || !String.IsNullOrEmpty(max))
434 return string.Format(GetString(MessageConstants.RangeTooHighOrLowMessage), min, max);
436 else
438 throw new InvalidOperationException();
442 /// <summary>
443 /// Returns the key used to internationalize error messages
444 /// </summary>
445 /// <value></value>
446 protected override string MessageKey
448 get { return MessageConstants.InvalidRangeMessage; }
451 private static void AssertValid(string max, string min)
453 if (String.IsNullOrEmpty(min) && String.IsNullOrEmpty(max))
455 throw new ArgumentException(
456 "Both min and max were set in such as way that neither would be tested. At least one must be tested.");
460 private static void AssertValid(int max, int min)
462 if (min == int.MinValue && max == int.MaxValue)
464 throw new ArgumentException(
465 "Both min and max were set in such as way that neither would be tested. At least one must be tested.");
467 if (min > max)
469 throw new ArgumentException("The min parameter must be less than or equal to the max parameter.");
471 if (max < min)
473 throw new ArgumentException("The max parameter must be greater than or equal to the min parameter.");
477 private static void AssertValid(decimal max, decimal min)
479 if (min == decimal.MinValue && max == decimal.MaxValue)
481 throw new ArgumentException(
482 "Both min and max were set in such as way that neither would be tested. At least one must be tested.");
484 if (min > max)
486 throw new ArgumentException("The min parameter must be less than or equal to the max parameter.");
488 if (max < min)
490 throw new ArgumentException("The max parameter must be greater than or equal to the min parameter.");
494 private static void AssertValid(DateTime max, DateTime min)
496 if (min == DateTime.MinValue && max == DateTime.MaxValue)
498 throw new ArgumentException(
499 "Both min and max were set in such as way that neither would be tested. At least one must be tested.");
501 if (min > max)
503 throw new ArgumentException("The min parameter must be less than or equal to the max parameter.");
505 if (max < min)
507 throw new ArgumentException("The max parameter must be greater than or equal to the min parameter.");
511 /// <summary>
512 /// Internal method that checks a given minimum value's data type and converts
513 /// null values to the proper minimum value for the data type.
514 /// </summary>
515 /// <param name="min">The minimum value to be processed.</param>
516 /// <returns>The minimum value with appropriate null-converted minimum values.</returns>
517 private object GetMinValue(object min)
521 //check properties for valid types
522 switch(type)
524 case RangeValidationType.Integer:
525 return GetIntValue(min, int.MinValue);
526 case RangeValidationType.Decimal:
527 return GetDecimalValue(min, decimal.MinValue);
528 case RangeValidationType.DateTime:
529 return GetDateTimeValue(min, DateTime.MinValue);
530 case RangeValidationType.String:
531 return (min == null || String.IsNullOrEmpty(min.ToString()) ? String.Empty : min.ToString());
532 default:
533 throw new ArgumentException("Unknown RangeValidatorType found.");
536 catch(InvalidCastException)
538 throw new ArgumentException(
539 "RangeValidator's mininum value data type is incompatible with the RangeValidationType specified.");
543 private int GetIntValue(object value, int defaultValue)
545 int intValue = defaultValue;
548 intValue = (int) value;
550 catch
552 if (value == null || !int.TryParse(value.ToString(), out intValue))
553 value = defaultValue;
555 return intValue;
558 private decimal GetDecimalValue(object value, decimal defaultValue)
560 decimal decimalValue = defaultValue;
561 if (value == null || !decimal.TryParse(value.ToString(), out decimalValue))
562 value = defaultValue;
563 return decimalValue;
566 private DateTime GetDateTimeValue(object value, DateTime defaultValue)
568 DateTime dtValue = defaultValue;
571 dtValue = (DateTime) value;
573 catch
575 if (value == null || !DateTime.TryParse(value.ToString(), out dtValue))
576 value = defaultValue;
578 return dtValue;