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>

View File

@@ -0,0 +1,169 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Collections.Generic;
namespace System
{
internal static partial class AppContextDefaultValues
{
public static void PopulateDefaultValues()
{
string platformIdentifier, profile;
int version;
ParseTargetFrameworkName(out platformIdentifier, out profile, out version);
// Call into each library to populate their default switches
PopulateDefaultValuesPartial(platformIdentifier, profile, version);
}
/// <summary>
/// We have this separate method for getting the parsed elements out of the TargetFrameworkName so we can
/// more easily support this on other platforms.
/// </summary>
private static void ParseTargetFrameworkName(out string identifier, out string profile, out int version)
{
string targetFrameworkMoniker = AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
// If we don't have a TFM then we should default to the 4.0 behavior where all quirks are turned on.
if (!TryParseFrameworkName(targetFrameworkMoniker, out identifier, out version, out profile))
{
#if FEATURE_CORECLR
if (CompatibilitySwitches.UseLatestBehaviorWhenTFMNotSpecified)
{
// If we want to use the latest behavior it is enough to set the value of the switch to string.Empty.
// When the get to the caller of this method (PopulateDefaultValuesPartial) we are going to use the
// identifier we just set to decide which switches to turn on. By having an empty string as the
// identifier we are simply saying -- don't turn on any switches, and we are going to get the latest
// behavior for all the switches
identifier = string.Empty;
}
else
#endif
{
identifier = ".NETFramework";
version = 40000;
profile = string.Empty;
}
}
}
// This code was a constructor copied from the FrameworkName class, which is located in System.dll.
// Parses strings in the following format: "<identifier>, Version=[v|V]<version>, Profile=<profile>"
// - The identifier and version is required, profile is optional
// - Only three components are allowed.
// - The version string must be in the System.Version format; an optional "v" or "V" prefix is allowed
private static bool TryParseFrameworkName(String frameworkName, out String identifier, out int version, out String profile)
{
// For parsing a target Framework moniker, from the FrameworkName class
const char c_componentSeparator = ',';
const char c_keyValueSeparator = '=';
const char c_versionValuePrefix = 'v';
const String c_versionKey = "Version";
const String c_profileKey = "Profile";
identifier = profile = string.Empty;
version = 0;
if (frameworkName == null || frameworkName.Length == 0)
{
return false;
}
String[] components = frameworkName.Split(c_componentSeparator);
version = 0;
// Identifer and Version are required, Profile is optional.
if (components.Length < 2 || components.Length > 3)
{
return false;
}
//
// 1) Parse the "Identifier", which must come first. Trim any whitespace
//
identifier = components[0].Trim();
if (identifier.Length == 0)
{
return false;
}
bool versionFound = false;
profile = null;
//
// The required "Version" and optional "Profile" component can be in any order
//
for (int i = 1; i < components.Length; i++)
{
// Get the key/value pair separated by '='
string[] keyValuePair = components[i].Split(c_keyValueSeparator);
if (keyValuePair.Length != 2)
{
return false;
}
// Get the key and value, trimming any whitespace
string key = keyValuePair[0].Trim();
string value = keyValuePair[1].Trim();
//
// 2) Parse the required "Version" key value
//
if (key.Equals(c_versionKey, StringComparison.OrdinalIgnoreCase))
{
versionFound = true;
// Allow the version to include a 'v' or 'V' prefix...
if (value.Length > 0 && (value[0] == c_versionValuePrefix || value[0] == 'V'))
{
value = value.Substring(1);
}
Version realVersion = new Version(value);
// The version class will represent some unset values as -1 internally (instead of 0).
version = realVersion.Major * 10000;
if (realVersion.Minor > 0)
version += realVersion.Minor * 100;
if (realVersion.Build > 0)
version += realVersion.Build;
}
//
// 3) Parse the optional "Profile" key value
//
else if (key.Equals(c_profileKey, StringComparison.OrdinalIgnoreCase))
{
if (!String.IsNullOrEmpty(value))
{
profile = value;
}
}
else
{
return false;
}
}
if (!versionFound)
{
return false;
}
return true;
}
// This is a partial method. Platforms (such as Desktop) can provide an implementation of it that will read override value
// from whatever mechanism is available on that platform. If no implementation is provided, the compiler is going to remove the calls
// to it from the code
static partial void TryGetSwitchOverridePartial(string switchName, ref bool overrideFound, ref bool overrideValue);
/// This is a partial method. This method is responsible for populating the default values based on a TFM.
/// It is partial because each library should define this method in their code to contain their defaults.
static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version);
}
}

View File

@@ -0,0 +1,128 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
namespace System
{
internal static partial class LocalAppContext
{
private delegate bool TryGetSwitchDelegate(string switchName, out bool value);
private static TryGetSwitchDelegate TryGetSwitchFromCentralAppContext;
private static bool s_canForwardCalls;
private static Dictionary<string, bool> s_switchMap = new Dictionary<string, bool>();
private static readonly object s_syncLock = new object();
private static bool DisableCaching { get; set; }
static LocalAppContext()
{
// Try to setup the callback into the central AppContext
s_canForwardCalls = SetupDelegate();
// Populate the default values of the local app context
AppContextDefaultValues.PopulateDefaultValues();
// Cache the value of the switch that help with testing
DisableCaching = IsSwitchEnabled(@"TestSwitch.LocalAppContext.DisableCaching");
}
public static bool IsSwitchEnabled(string switchName)
{
if (s_canForwardCalls)
{
bool isEnabledCentrally;
if (TryGetSwitchFromCentralAppContext(switchName, out isEnabledCentrally))
{
// we found the switch, so return whatever value it has
return isEnabledCentrally;
}
// if we could not get the value from the central authority, try the local storage.
}
return IsSwitchEnabledLocal(switchName);
}
private static bool IsSwitchEnabledLocal(string switchName)
{
// read the value from the set of local defaults
bool isEnabled, isPresent;
lock (s_switchMap)
{
isPresent = s_switchMap.TryGetValue(switchName, out isEnabled);
}
// If the value is in the set of local switches, reutrn the value
if (isPresent)
{
return isEnabled;
}
// if we could not find the switch name, we should return 'false'
// This will preserve the concept of switches been 'off' unless explicitly set to 'on'
return false;
}
private static bool SetupDelegate()
{
Type appContextType = typeof(object).Assembly.GetType("System.AppContext");
if (appContextType == null)
return false;
MethodInfo method = appContextType.GetMethod(
"TryGetSwitch", // the method name
BindingFlags.Static | BindingFlags.Public, // binding flags
null, // use the default binder
new Type[] { typeof(string), typeof(bool).MakeByRefType() },
null); // parameterModifiers - this is ignored by the default binder
if (method == null)
return false;
// Create delegate if we found the method.
TryGetSwitchFromCentralAppContext = (TryGetSwitchDelegate)Delegate.CreateDelegate(typeof(TryGetSwitchDelegate), method);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool GetCachedSwitchValue(string switchName, ref int switchValue)
{
if (switchValue < 0) return false;
if (switchValue > 0) return true;
return GetCachedSwitchValueInternal(switchName, ref switchValue);
}
private static bool GetCachedSwitchValueInternal(string switchName, ref int switchValue)
{
if (LocalAppContext.DisableCaching)
{
return LocalAppContext.IsSwitchEnabled(switchName);
}
bool isEnabled = LocalAppContext.IsSwitchEnabled(switchName);
switchValue = isEnabled ? 1 /*true*/ : -1 /*false*/;
return isEnabled;
}
/// <summary>
/// This method is going to be called from the AppContextDefaultValues class when setting up the
/// default values for the switches. !!!! This method is called during the static constructor so it does not
/// take a lock !!!! If you are planning to use this outside of that, please ensure proper locking.
/// </summary>
internal static void DefineSwitchDefault(string switchName, bool initialValue)
{
s_switchMap[switchName] = initialValue;
}
}
}

View File

@@ -32,4 +32,4 @@ using System.Security;
[assembly: CLSCompliant(true)]
[assembly: AssemblyVersion("99.0.0.0")]
[assembly: NeutralResourcesLanguage("en-US")]
#endif
#endif

View File

@@ -18,3 +18,6 @@ ArgumentIsNullOrWhitespace=The argument '{0}' cannot be null, empty or contain o
RequiredAttribute_ValidationError=The {0} field is required.
CompareAttribute_MustMatch='{0}' and '{1}' do not match.
RegexAttribute_ValidationError=The field {0} must match the regular expression '{1}'.