Imported Upstream version 4.3.2.467

Former-commit-id: 9c2cb47f45fa221e661ab616387c9cda183f283d
This commit is contained in:
Xamarin Public Jenkins
2016-02-22 11:00:01 -05:00
parent f302175246
commit f3e3aab35a
4097 changed files with 122406 additions and 82300 deletions

View File

@@ -0,0 +1,35 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System
{
using System;
using System.ComponentModel.DataAnnotations;
internal static partial class AppContextDefaultValues
{
static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version)
{
// When defining a new switch you should add it to the last known version.
// For instance, if you are adding a switch in .NET 4.6 (the release after 4.5.2) you should defined your switch
// like this:
// if (version <= 40502) ...
// This ensures that all previous versions of that platform (up-to 4.5.2) will get the old behavior by default
// NOTE: When adding a default value for a switch please make sure that the default value is added to ALL of the existing platforms!
// NOTE: When adding a new if statement for the version please ensure that ALL previous switches are enabled (ie. don't use else if)
switch (platformIdentifier)
{
case ".NETCore":
case ".NETFramework":
{
if (version <= 40600)
{
LocalAppContextSwitches.SetDefaultsLessOrEqual_46();
}
break;
}
}
}
}
}

View File

@@ -0,0 +1,52 @@
//------------------------------------------------------------------------------
// <copyright file="AppSettings.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
// AppSettings.cs
//
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
namespace System.ComponentModel.DataAnnotations {
internal static class AppSettings {
#if MONO
internal static readonly bool DisableRegEx = false;
#else
private static volatile bool _settingsInitialized = false;
private static object _appSettingsLock = new object();
private static void EnsureSettingsLoaded() {
if (!_settingsInitialized) {
lock (_appSettingsLock) {
if (!_settingsInitialized) {
NameValueCollection settings = null;
try {
settings = ConfigurationManager.AppSettings;
}
catch (ConfigurationErrorsException) { }
finally {
if (settings == null || !Boolean.TryParse(settings["dataAnnotations:dataTypeAttribute:disableRegEx"], out _disableRegEx))
_disableRegEx = false;
_settingsInitialized = true;
}
}
}
}
}
private static bool _disableRegEx;
internal static bool DisableRegEx {
get {
EnsureSettingsLoaded();
return _disableRegEx;
}
}
#endif
}
}

View File

@@ -9,7 +9,10 @@
public sealed class CreditCardAttribute : DataTypeAttribute {
public CreditCardAttribute()
: base(DataType.CreditCard) {
ErrorMessage = DataAnnotationsResources.CreditCardAttribute_Invalid;
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.CreditCardAttribute_Invalid;
}
public override bool IsValid(object value) {

View File

@@ -8,11 +8,14 @@
// This attribute provides server-side email validation equivalent to jquery validate,
// and therefore shares the same regular expression. See unit tests for examples.
private static Regex _regex = new Regex(@"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
private static Regex _regex = CreateRegEx();
public EmailAddressAttribute()
: base(DataType.EmailAddress) {
ErrorMessage = DataAnnotationsResources.EmailAddressAttribute_Invalid;
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.EmailAddressAttribute_Invalid;
}
public override bool IsValid(object value) {
@@ -21,7 +24,51 @@
}
string valueAsString = value as string;
return valueAsString != null && _regex.Match(valueAsString).Length > 0;
// Use RegEx implementation if it has been created, otherwise use a non RegEx version.
if (_regex != null) {
return valueAsString != null && _regex.Match(valueAsString).Length > 0;
}
else {
int atCount = 0;
foreach (char c in valueAsString) {
if (c == '@') {
atCount++;
}
}
return (valueAsString != null
&& atCount == 1
&& valueAsString[0] != '@'
&& valueAsString[valueAsString.Length - 1] != '@');
}
}
private static Regex CreateRegEx() {
// We only need to create the RegEx if this switch is enabled.
if (AppSettings.DisableRegEx) {
return null;
}
const string pattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
// Set explicit regex match timeout, sufficient enough for email parsing
// Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set
TimeSpan matchTimeout = TimeSpan.FromSeconds(2);
try {
if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) {
return new Regex(pattern, options, matchTimeout);
}
}
catch {
// Fallback on error
}
// Legacy fallback (without explicit match timeout)
return new Regex(pattern, options);
}
}
}

View File

@@ -14,7 +14,10 @@ namespace System.ComponentModel.DataAnnotations {
public FileExtensionsAttribute()
: base(DataType.Upload) {
ErrorMessage = DataAnnotationsResources.FileExtensionsAttribute_Invalid;
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.FileExtensionsAttribute_Invalid;
}
public string Extensions {

View File

@@ -8,4 +8,4 @@ namespace System.ComponentModel.DataAnnotations {
IEnumerable<ValidationResult> Validate(ValidationContext validationContext);
}
}
#endif
#endif

View File

@@ -0,0 +1,28 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.ComponentModel.DataAnnotations {
using System;
using System.Runtime.CompilerServices;
// When adding a quirk, name it such that false is new behavior and true is old behavior.
// You are opting IN to old behavior. The new behavior is default.
// For example, we don't want to use legacy regex timeout for RegularExpressionAttribute in 4.6.1+.
// So we set UseLegacyRegExTimeout to true if running 4.6 or less.
internal static class LocalAppContextSwitches {
private const string UseLegacyRegExTimeoutString = "Switch.System.ComponentModel.DataAnnotations.RegularExpressionAttribute.UseLegacyRegExTimeout";
private static int useLegacyRegExTimeout;
public static bool UseLegacyRegExTimeout {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get {
return LocalAppContext.GetCachedSwitchValue(UseLegacyRegExTimeoutString, ref useLegacyRegExTimeout);
}
}
public static void SetDefaultsLessOrEqual_46() {
// Define the switches that should be true for 4.6 or less, false for 4.6.1+.
LocalAppContext.DefineSwitchDefault(UseLegacyRegExTimeoutString, true);
}
}
}

View File

@@ -6,11 +6,15 @@
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class PhoneAttribute : DataTypeAttribute {
// see unit tests for examples
private static Regex _regex = new Regex(@"^(\+\s?)?((?<!\+.*)\(\+?\d+([\s\-\.]?\d+)?\)|\d+)([\s\-\.]?(\(\d+([\s\-\.]?\d+)?\)|\d+))*(\s?(x|ext\.?)\s?\d+)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
private static Regex _regex = CreateRegEx();
private const string _additionalPhoneNumberCharacters = "-.()";
public PhoneAttribute()
: base(DataType.PhoneNumber) {
ErrorMessage = DataAnnotationsResources.PhoneAttribute_Invalid;
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.PhoneAttribute_Invalid;
}
public override bool IsValid(object value) {
@@ -19,7 +23,114 @@
}
string valueAsString = value as string;
return valueAsString != null && _regex.Match(valueAsString).Length > 0;
// Use RegEx implementation if it has been created, otherwise use a non RegEx version.
if (_regex != null) {
return valueAsString != null && _regex.Match(valueAsString).Length > 0;
}
else {
if (valueAsString == null) {
return false;
}
valueAsString = valueAsString.Replace("+", string.Empty).TrimEnd();
valueAsString = RemoveExtension(valueAsString);
bool digitFound = false;
foreach (char c in valueAsString) {
if (Char.IsDigit(c)) {
digitFound = true;
break;
}
}
if (!digitFound) {
return false;
}
foreach (char c in valueAsString)
{
if (!(Char.IsDigit(c)
|| Char.IsWhiteSpace(c)
|| _additionalPhoneNumberCharacters.IndexOf(c) != -1)) {
return false;
}
}
return true;
}
}
private static Regex CreateRegEx() {
// We only need to create the RegEx if this switch is enabled.
if (AppSettings.DisableRegEx) {
return null;
}
const string pattern = @"^(\+\s?)?((?<!\+.*)\(\+?\d+([\s\-\.]?\d+)?\)|\d+)([\s\-\.]?(\(\d+([\s\-\.]?\d+)?\)|\d+))*(\s?(x|ext\.?)\s?\d+)?$";
const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
// Set explicit regex match timeout, sufficient enough for phone parsing
// Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set
TimeSpan matchTimeout = TimeSpan.FromSeconds(2);
try {
if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) {
return new Regex(pattern, options, matchTimeout);
}
}
catch {
// Fallback on error
}
// Legacy fallback (without explicit match timeout)
return new Regex(pattern, options);
}
private static string RemoveExtension(string potentialPhoneNumber) {
int lastIndexOfExtension = potentialPhoneNumber
.LastIndexOf("ext.", StringComparison.InvariantCultureIgnoreCase);
if (lastIndexOfExtension >= 0) {
string extension = potentialPhoneNumber.Substring(lastIndexOfExtension + 4);
if (MatchesExtension(extension)) {
return potentialPhoneNumber.Substring(0, lastIndexOfExtension);
}
}
lastIndexOfExtension = potentialPhoneNumber
.LastIndexOf("ext", StringComparison.InvariantCultureIgnoreCase);
if (lastIndexOfExtension >= 0) {
string extension = potentialPhoneNumber.Substring(lastIndexOfExtension + 3);
if (MatchesExtension(extension)) {
return potentialPhoneNumber.Substring(0, lastIndexOfExtension);
}
}
lastIndexOfExtension = potentialPhoneNumber
.LastIndexOf("x", StringComparison.InvariantCultureIgnoreCase);
if (lastIndexOfExtension >= 0) {
string extension = potentialPhoneNumber.Substring(lastIndexOfExtension + 1);
if (MatchesExtension(extension)) {
return potentialPhoneNumber.Substring(0, lastIndexOfExtension);
}
}
return potentialPhoneNumber;
}
private static bool MatchesExtension(string potentialExtension) {
potentialExtension = potentialExtension.TrimStart();
if (potentialExtension.Length == 0) {
return false;
}
foreach (char c in potentialExtension) {
if (!Char.IsDigit(c)) {
return false;
}
}
return true;
}
}
}

View File

@@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations.Resources;
using System.ComponentModel.DataAnnotations.Resources;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
@@ -15,6 +15,12 @@ namespace System.ComponentModel.DataAnnotations {
/// </summary>
public string Pattern { get; private set; }
/// <summary>
/// Gets or sets the timeout to use when matching the regular expression pattern (in milliseconds)
/// (-1 means never timeout).
/// </summary>
public int MatchTimeoutInMilliseconds { get; set; } = GetDefaultTimeout();
private Regex Regex { get; set; }
/// <summary>
@@ -77,13 +83,33 @@ namespace System.ComponentModel.DataAnnotations {
/// </summary>
/// <exception cref="ArgumentException"> is thrown if the current <see cref="Pattern"/> cannot be parsed</exception>
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception>
/// <exception cref="ArgumentOutOfRangeException"> thrown if <see cref="MatchTimeoutInMilliseconds" /> is negative (except -1),
/// zero or greater than approximately 24 days </exception>
private void SetupRegex() {
if (this.Regex == null) {
if (string.IsNullOrEmpty(this.Pattern)) {
throw new InvalidOperationException(DataAnnotationsResources.RegularExpressionAttribute_Empty_Pattern);
}
this.Regex = new Regex(this.Pattern);
Regex = MatchTimeoutInMilliseconds == -1
? new Regex(Pattern)
: Regex = new Regex(Pattern, default(RegexOptions), TimeSpan.FromMilliseconds((double)MatchTimeoutInMilliseconds));
}
}
/// <summary>
/// Returns the default MatchTimeout based on UseLegacyRegExTimeout switch.
/// </summary>
private static int GetDefaultTimeout() {
#if !MONO
if (LocalAppContextSwitches.UseLegacyRegExTimeout) {
return -1;
}
else
#endif
{
return 2000;
}
}
}
}

View File

@@ -10,4 +10,4 @@ namespace System.ComponentModel.DataAnnotations.Schema {
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public class ComplexTypeAttribute : Attribute {
}
}
}

View File

@@ -18,4 +18,4 @@ namespace System.ComponentModel.DataAnnotations.Schema {
/// </summary>
Computed
}
}
}

View File

@@ -8,4 +8,4 @@ namespace System.ComponentModel.DataAnnotations.Schema {
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public class NotMappedAttribute : Attribute {
}
}
}

View File

@@ -8,11 +8,14 @@
// This attribute provides server-side url validation equivalent to jquery validate,
// and therefore shares the same regular expression. See unit tests for examples.
private static Regex _regex = new Regex(@"^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
private static Regex _regex = CreateRegEx();
public UrlAttribute()
: base(DataType.Url) {
ErrorMessage = DataAnnotationsResources.UrlAttribute_Invalid;
// DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set
// ErrorMessageResourceType and ErrorMessageResourceName to use localized messages.
DefaultErrorMessage = DataAnnotationsResources.UrlAttribute_Invalid;
}
public override bool IsValid(object value) {
@@ -21,7 +24,44 @@
}
string valueAsString = value as string;
return valueAsString != null && _regex.Match(valueAsString).Length > 0;
// Use RegEx implementation if it has been created, otherwise use a non RegEx version.
if (_regex != null) {
return valueAsString != null && _regex.Match(valueAsString).Length > 0;
}
else {
return valueAsString != null &&
(valueAsString.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)
|| valueAsString.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)
|| valueAsString.StartsWith("ftp://", StringComparison.InvariantCultureIgnoreCase));
}
}
private static Regex CreateRegEx() {
// We only need to create the RegEx if this switch is enabled.
if (AppSettings.DisableRegEx) {
return null;
}
const string pattern = @"^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$";
const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
// Set explicit regex match timeout, sufficient enough for url parsing
// Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set
TimeSpan matchTimeout = TimeSpan.FromSeconds(2);
try {
if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) {
return new Regex(pattern, options, matchTimeout);
}
}
catch {
// Fallback on error
}
// Legacy fallback (without explicit match timeout)
return new Regex(pattern, options);
}
}
}

View File

@@ -21,8 +21,9 @@ namespace System.ComponentModel.DataAnnotations {
private Func<string> _errorMessageResourceAccessor;
private string _errorMessageResourceName;
private Type _errorMessageResourceType;
private bool _isCallingOverload;
private object _syncLock = new object();
private string _defaultErrorMessage;
private volatile bool _hasBaseIsValid;
#endregion
@@ -60,6 +61,31 @@ namespace System.ComponentModel.DataAnnotations {
#endregion
#region Internal Properties
/// <summary>
/// Gets or sets and the default error message string.
/// This message will be used if the user has not set <see cref="ErrorMessage"/>
/// or the <see cref="ErrorMessageResourceType"/> and <see cref="ErrorMessageResourceName"/> pair.
/// This property was added after the public contract for DataAnnotations was created.
/// It was added to fix DevDiv issue 468241.
/// It is internal to avoid changing the DataAnnotations contract.
/// </summary>
internal string DefaultErrorMessage
{
get
{
return this._defaultErrorMessage;
}
set
{
this._defaultErrorMessage = value;
this._errorMessageResourceAccessor = null;
this.CustomErrorMessageSet = true;
}
}
#endregion
#region Protected Properties
/// <summary>
@@ -75,7 +101,7 @@ namespace System.ComponentModel.DataAnnotations {
/// <summary>
/// A flag indicating whether a developer has customized the attribute's error message by setting any one of
/// ErrorMessage, ErrorMessageResourceName, or ErrorMessageResourceType.
/// ErrorMessage, ErrorMessageResourceName, ErrorMessageResourceType or DefaultErrorMessage.
/// </summary>
internal bool CustomErrorMessageSet {
get;
@@ -105,12 +131,23 @@ namespace System.ComponentModel.DataAnnotations {
/// </value>
public string ErrorMessage {
get {
return this._errorMessage;
// DevDiv: 468241
// If _errorMessage is not set, return the default. This is done to preserve
// behavior prior to the fix where ErrorMessage showed the non-null message to use.
return this._errorMessage ?? this._defaultErrorMessage;
}
set {
this._errorMessage = value;
this._errorMessageResourceAccessor = null;
this.CustomErrorMessageSet = true;
// DevDiv: 468241
// Explicitly setting ErrorMessage also sets DefaultErrorMessage if null.
// This prevents subsequent read of ErrorMessage from returning default.
if (value == null)
{
this._defaultErrorMessage = null;
}
}
}
@@ -163,14 +200,16 @@ namespace System.ComponentModel.DataAnnotations {
/// <exception cref="InvalidOperationException"> is thrown if the current attribute is malformed.</exception>
private void SetupResourceAccessor() {
if (this._errorMessageResourceAccessor == null) {
string localErrorMessage = this._errorMessage;
string localErrorMessage = this.ErrorMessage;
bool resourceNameSet = !string.IsNullOrEmpty(this._errorMessageResourceName);
bool errorMessageSet = !string.IsNullOrEmpty(localErrorMessage);
bool errorMessageSet = !string.IsNullOrEmpty(this._errorMessage);
bool resourceTypeSet = this._errorMessageResourceType != null;
bool defaultMessageSet = !string.IsNullOrEmpty(this._defaultErrorMessage);
// Either ErrorMessageResourceName or ErrorMessage may be set, but not both.
// The following test checks both being set as well as both being not set.
if (resourceNameSet == errorMessageSet) {
// The following combinations are illegal and throw InvalidOperationException:
// 1) Both ErrorMessage and ErrorMessageResourceName are set, or
// 2) None of ErrorMessage, ErrorMessageReourceName, and DefaultErrorMessage are set.
if ((resourceNameSet && errorMessageSet) || !(resourceNameSet || errorMessageSet || defaultMessageSet)) {
throw new InvalidOperationException(DataAnnotationsResources.ValidationAttribute_Cannot_Set_ErrorMessage_And_Resource);
}
@@ -281,20 +320,14 @@ namespace System.ComponentModel.DataAnnotations {
#else
internal
#endif
virtual bool IsValid(object value) {
lock (this._syncLock) {
if (this._isCallingOverload) {
throw new NotImplementedException(DataAnnotationsResources.ValidationAttribute_IsValid_NotImplemented);
} else {
this._isCallingOverload = true;
try {
return this.IsValid(value, null) == null;
} finally {
this._isCallingOverload = false;
}
}
virtual bool IsValid(object value) {
if(!this._hasBaseIsValid) {
// track that this method overload has not been overridden.
this._hasBaseIsValid = true;
}
// call overridden method.
return this.IsValid(value, null) == null;
}
#if !SILVERLIGHT
@@ -336,25 +369,20 @@ namespace System.ComponentModel.DataAnnotations {
/// </exception>
#endif
protected virtual ValidationResult IsValid(object value, ValidationContext validationContext) {
lock (this._syncLock) {
if (this._isCallingOverload) {
throw new NotImplementedException(DataAnnotationsResources.ValidationAttribute_IsValid_NotImplemented);
} else {
this._isCallingOverload = true;
if (this._hasBaseIsValid) {
// this means neither of the IsValid methods has been overriden, throw.
throw new NotImplementedException(DataAnnotationsResources.ValidationAttribute_IsValid_NotImplemented);
}
ValidationResult result = ValidationResult.Success;
try {
ValidationResult result = ValidationResult.Success;
if (!this.IsValid(value)) {
string[] memberNames = validationContext.MemberName != null ? new string[] { validationContext.MemberName } : null;
result = new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName), memberNames);
}
return result;
} finally {
this._isCallingOverload = false;
}
}
// call overridden method.
if (!this.IsValid(value)) {
string[] memberNames = validationContext.MemberName != null ? new string[] { validationContext.MemberName } : null;
result = new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName), memberNames);
}
return result;
}
/// <summary>