|
//------------------------------------------------------------------------------
// <copyright file="basecomparevalidator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.UI.WebControls {
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Text.RegularExpressions;
using System.Text;
using System.Web.Util;
/// <devdoc>
/// <para> Serves as the abstract base
/// class for validators that do typed comparisons.</para>
/// </devdoc>
public abstract class BaseCompareValidator : BaseValidator {
/// <devdoc>
/// <para>Gets or sets the data type that specifies how the values
/// being compared should be interpreted.</para>
/// </devdoc>
[
WebCategory("Behavior"),
Themeable(false),
DefaultValue(ValidationDataType.String),
WebSysDescription(SR.RangeValidator_Type)
]
public ValidationDataType Type {
get {
object o = ViewState["Type"];
return((o == null) ? ValidationDataType.String : (ValidationDataType)o);
}
set {
if (value < ValidationDataType.String || value > ValidationDataType.Currency) {
throw new ArgumentOutOfRangeException("value");
}
ViewState["Type"] = value;
}
}
/// <devdoc>
/// Whether we should do culture invariant conversion against the
/// string value properties on the control
/// </devdoc>
[
WebCategory("Behavior"),
Themeable(false),
DefaultValue(false),
WebSysDescription(SR.BaseCompareValidator_CultureInvariantValues)
]
public bool CultureInvariantValues {
get {
object o = ViewState["CultureInvariantValues"];
return((o == null) ? false : (bool)o);
}
set {
ViewState["CultureInvariantValues"] = value;
}
}
/// <internalonly/>
/// <devdoc>
/// AddAttributesToRender method
/// </devdoc>
protected override void AddAttributesToRender(HtmlTextWriter writer) {
base.AddAttributesToRender(writer);
if (RenderUplevel) {
ValidationDataType type = Type;
if (type != ValidationDataType.String) {
string id = ClientID;
HtmlTextWriter expandoAttributeWriter = (EnableLegacyRendering || IsUnobtrusive) ? writer : null;
AddExpandoAttribute(expandoAttributeWriter, id, "type", PropertyConverter.EnumToString(typeof(ValidationDataType), type), false);
NumberFormatInfo info = NumberFormatInfo.CurrentInfo;
if (type == ValidationDataType.Double) {
string decimalChar = info.NumberDecimalSeparator;
AddExpandoAttribute(expandoAttributeWriter, id, "decimalchar", decimalChar);
}
else if (type == ValidationDataType.Currency) {
string decimalChar = info.CurrencyDecimalSeparator;
AddExpandoAttribute(expandoAttributeWriter, id, "decimalchar", decimalChar);
string groupChar = info.CurrencyGroupSeparator;
// Map non-break space onto regular space for parsing
if (groupChar[0] == 160)
groupChar = " ";
AddExpandoAttribute(expandoAttributeWriter, id, "groupchar", groupChar);
int digits = info.CurrencyDecimalDigits;
AddExpandoAttribute(expandoAttributeWriter, id, "digits", digits.ToString(NumberFormatInfo.InvariantInfo), false);
// VSWhidbey 83165
int groupSize = GetCurrencyGroupSize(info);
if (groupSize > 0) {
AddExpandoAttribute(expandoAttributeWriter, id, "groupsize", groupSize.ToString(NumberFormatInfo.InvariantInfo), false);
}
}
else if (type == ValidationDataType.Date) {
AddExpandoAttribute(expandoAttributeWriter, id, "dateorder", GetDateElementOrder(), false);
AddExpandoAttribute(expandoAttributeWriter, id, "cutoffyear", CutoffYear.ToString(NumberFormatInfo.InvariantInfo), false);
// VSWhidbey 504553: The changes of this bug make client-side script not
// using the century attribute anymore, but still generating it for
// backward compatibility with Everett pages.
int currentYear = DateTime.Today.Year;
int century = currentYear - (currentYear % 100);
AddExpandoAttribute(expandoAttributeWriter, id, "century", century.ToString(NumberFormatInfo.InvariantInfo), false);
}
}
}
}
/// <devdoc>
/// Check if the text can be converted to the type
/// </devdoc>
public static bool CanConvert(string text, ValidationDataType type) {
return CanConvert(text, type, false);
}
public static bool CanConvert(string text, ValidationDataType type, bool cultureInvariant) {
object value = null;
return Convert(text, type, cultureInvariant, out value);
}
/// <internalonly/>
/// <devdoc>
/// Return the order of date elements for the current culture
/// </devdoc>
protected static string GetDateElementOrder() {
DateTimeFormatInfo info = DateTimeFormatInfo.CurrentInfo;
string shortPattern = info.ShortDatePattern;
if (shortPattern.IndexOf('y') < shortPattern.IndexOf('M')) {
return "ymd";
}
else if (shortPattern.IndexOf('M') < shortPattern.IndexOf('d')) {
return "mdy";
}
else {
return "dmy";
}
}
// VSWhidbey 83165
private static int GetCurrencyGroupSize(NumberFormatInfo info) {
int [] groupSizes = info.CurrencyGroupSizes;
if (groupSizes != null && groupSizes.Length == 1) {
return groupSizes[0];
}
else {
return -1;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected static int CutoffYear {
get {
return DateTimeFormatInfo.CurrentInfo.Calendar.TwoDigitYearMax;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected static int GetFullYear(int shortYear) {
Debug.Assert(shortYear >= 0 && shortYear < 100);
return DateTimeFormatInfo.CurrentInfo.Calendar.ToFourDigitYear(shortYear);
}
/// <devdoc>
/// Try to convert the test into the validation data type
/// </devdoc>
protected static bool Convert(string text, ValidationDataType type, out object value) {
return Convert(text, type, false, out value);
}
protected static bool Convert(string text, ValidationDataType type, bool cultureInvariant, out object value) {
value = null;
try {
switch (type) {
case ValidationDataType.String:
value = text;
break;
case ValidationDataType.Integer:
value = Int32.Parse(text, CultureInfo.InvariantCulture);
break;
case ValidationDataType.Double: {
string cleanInput;
if (cultureInvariant) {
cleanInput = ConvertDouble(text, CultureInfo.InvariantCulture.NumberFormat);
}
else {
cleanInput = ConvertDouble(text, NumberFormatInfo.CurrentInfo);
}
if (cleanInput != null) {
value = Double.Parse(cleanInput, CultureInfo.InvariantCulture);
}
break;
}
case ValidationDataType.Date: {
if (cultureInvariant) {
value = ConvertDate(text, "ymd");
}
else {
// if the calendar is not gregorian, we should not enable client-side, so just parse it directly:
if (!(DateTimeFormatInfo.CurrentInfo.Calendar.GetType() == typeof(GregorianCalendar))) {
value = DateTime.Parse(text, CultureInfo.CurrentCulture);
break;
}
string dateElementOrder = GetDateElementOrder();
value = ConvertDate(text, dateElementOrder);
}
break;
}
case ValidationDataType.Currency: {
string cleanInput;
if (cultureInvariant) {
cleanInput = ConvertCurrency(text, CultureInfo.InvariantCulture.NumberFormat);
}
else {
cleanInput = ConvertCurrency(text, NumberFormatInfo.CurrentInfo);
}
if (cleanInput != null) {
value = Decimal.Parse(cleanInput, CultureInfo.InvariantCulture);
}
break;
}
}
}
catch {
value = null;
}
return (value != null);
}
private static string ConvertCurrency(string text, NumberFormatInfo info) {
string decimalChar = info.CurrencyDecimalSeparator;
string groupChar = info.CurrencyGroupSeparator;
// VSWhidbey 83165
string beginGroupSize, subsequentGroupSize;
int groupSize = GetCurrencyGroupSize(info);
if (groupSize > 0) {
string groupSizeText = groupSize.ToString(NumberFormatInfo.InvariantInfo);
beginGroupSize = "{1," + groupSizeText + "}";
subsequentGroupSize = "{" + groupSizeText + "}";
}
else {
beginGroupSize = subsequentGroupSize = "+";
}
// Map non-break space onto regular space for parsing
if (groupChar[0] == 160)
groupChar = " ";
int digits = info.CurrencyDecimalDigits;
bool hasDigits = (digits > 0);
string currencyExpression =
"^\\s*([-\\+])?((\\d" + beginGroupSize + "(\\" + groupChar + "\\d" + subsequentGroupSize + ")+)|\\d*)"
+ (hasDigits ? "\\" + decimalChar + "?(\\d{0," + digits.ToString(NumberFormatInfo.InvariantInfo) + "})" : string.Empty)
+ "\\s*$";
Match m = Regex.Match(text, currencyExpression);
if (!m.Success) {
return null;
}
// Make sure there are some valid digits
if (m.Groups[2].Length == 0 && hasDigits && m.Groups[5].Length == 0) {
return null;
}
return m.Groups[1].Value
+ m.Groups[2].Value.Replace(groupChar, string.Empty)
+ ((hasDigits && m.Groups[5].Length > 0) ? "." + m.Groups[5].Value : string.Empty);
}
private static string ConvertDouble(string text, NumberFormatInfo info) {
// VSWhidbey 83156: If text is empty, it would be default to 0 for
// backward compatibility reason.
if (text.Length == 0) {
return "0";
}
string decimalChar = info.NumberDecimalSeparator;
string doubleExpression = "^\\s*([-\\+])?(\\d*)\\" + decimalChar + "?(\\d*)\\s*$";
Match m = Regex.Match(text, doubleExpression);
if (!m.Success) {
return null;
}
// Make sure there are some valid digits
if (m.Groups[2].Length == 0 && m.Groups[3].Length == 0) {
return null;
}
return m.Groups[1].Value
+ (m.Groups[2].Length > 0 ? m.Groups[2].Value : "0")
+ ((m.Groups[3].Length > 0) ? "." + m.Groups[3].Value: string.Empty);
}
// ****************************************************************************************************************
// ** **
// ** NOTE: When updating the regular expressions in this method, you must also update the regular expressions **
// ** in WebUIValidation.js::ValidatorConvert(). The server and client regular expressions must match. **
// ** **
// ****************************************************************************************************************
private static object ConvertDate(string text, string dateElementOrder) {
// always allow the YMD format, if they specify 4 digits
string dateYearFirstExpression = "^\\s*((\\d{4})|(\\d{2}))([-/]|\\. ?)(\\d{1,2})\\4(\\d{1,2})\\.?\\s*$";
Match m = Regex.Match(text, dateYearFirstExpression);
int day, month, year;
if (m.Success && (m.Groups[2].Success || dateElementOrder == "ymd")) {
day = Int32.Parse(m.Groups[6].Value, CultureInfo.InvariantCulture);
month = Int32.Parse(m.Groups[5].Value, CultureInfo.InvariantCulture);
if (m.Groups[2].Success) {
year = Int32.Parse(m.Groups[2].Value, CultureInfo.InvariantCulture);
}
else {
year = GetFullYear(Int32.Parse(m.Groups[3].Value, CultureInfo.InvariantCulture));
}
}
else {
if (dateElementOrder == "ymd") {
return null;
}
// also check for the year last format
string dateYearLastExpression = "^\\s*(\\d{1,2})([-/]|\\. ?)(\\d{1,2})(?:\\s|\\2)((\\d{4})|(\\d{2}))(?:\\s\u0433\\.|\\.)?\\s*$";
m = Regex.Match(text, dateYearLastExpression);
if (!m.Success) {
return null;
}
if (dateElementOrder == "mdy") {
day = Int32.Parse(m.Groups[3].Value, CultureInfo.InvariantCulture);
month = Int32.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture);
}
else {
day = Int32.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture);
month = Int32.Parse(m.Groups[3].Value, CultureInfo.InvariantCulture);
}
if (m.Groups[5].Success) {
year = Int32.Parse(m.Groups[5].Value, CultureInfo.InvariantCulture);
} else {
year = GetFullYear(Int32.Parse(m.Groups[6].Value, CultureInfo.InvariantCulture));
}
}
return new DateTime(year, month, day);
}
/// <devdoc>
/// Compare two strings using the type and operator
/// </devdoc>
protected static bool Compare(string leftText, string rightText, ValidationCompareOperator op, ValidationDataType type) {
return Compare(leftText, false, rightText, false, op, type);
}
protected static bool Compare(string leftText, bool cultureInvariantLeftText,
string rightText, bool cultureInvariantRightText,
ValidationCompareOperator op, ValidationDataType type) {
object leftObject;
if (!Convert(leftText, type, cultureInvariantLeftText, out leftObject))
return false;
if (op == ValidationCompareOperator.DataTypeCheck)
return true;
object rightObject;
if (!Convert(rightText, type, cultureInvariantRightText, out rightObject))
return true;
int compareResult;
switch (type) {
case ValidationDataType.String:
compareResult = String.Compare((string)leftObject, (string) rightObject, false, CultureInfo.CurrentCulture);
break;
case ValidationDataType.Integer:
compareResult = ((int)leftObject).CompareTo(rightObject);
break;
case ValidationDataType.Double:
compareResult = ((double)leftObject).CompareTo(rightObject);
break;
case ValidationDataType.Date:
compareResult = ((DateTime)leftObject).CompareTo(rightObject);
break;
case ValidationDataType.Currency:
compareResult = ((Decimal)leftObject).CompareTo(rightObject);
break;
default:
Debug.Fail("Unknown Type");
return true;
}
switch (op) {
case ValidationCompareOperator.Equal:
return compareResult == 0;
case ValidationCompareOperator.NotEqual:
return compareResult != 0;
case ValidationCompareOperator.GreaterThan:
return compareResult > 0 ;
case ValidationCompareOperator.GreaterThanEqual:
return compareResult >= 0 ;
case ValidationCompareOperator.LessThan:
return compareResult < 0 ;
case ValidationCompareOperator.LessThanEqual:
return compareResult <= 0 ;
default:
Debug.Fail("Unknown Operator");
return true;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override bool DetermineRenderUplevel() {
// We don't do client-side validation for dates with non gregorian calendars
if (Type == ValidationDataType.Date && DateTimeFormatInfo.CurrentInfo.Calendar.GetType() != typeof(GregorianCalendar)) {
return false;
}
return base.DetermineRenderUplevel();
}
internal string ConvertToShortDateString(string text) {
// VSWhidbey 83099, 85305, we should ignore error if it happens and
// leave text as intact when parsing the date. We assume the caller
// (validator) is able to handle invalid text itself.
DateTime date;
if (DateTime.TryParse(text, CultureInfo.CurrentCulture, DateTimeStyles.None, out date)) {
text = date.ToShortDateString();
}
return text;
}
internal bool IsInStandardDateFormat(string date) {
// VSWhidbey 115454: We identify that date string with only numbers
// and specific punctuation separators is in standard date format.
const string standardDateExpression = "^\\s*(\\d+)([-/]|\\. ?)(\\d+)\\2(\\d+)\\s*$";
return Regex.Match(date, standardDateExpression).Success;
}
internal string ConvertCultureInvariantToCurrentCultureFormat(string valueInString,
ValidationDataType type) {
object value;
Convert(valueInString, type, true, out value);
if (value is DateTime) {
// For Date type we explicitly want the date portion only
return ((DateTime) value).ToShortDateString();
}
else {
return System.Convert.ToString(value, CultureInfo.CurrentCulture);
}
}
}
}
|