1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
namespace System.ComponentModel.DataAnnotations {
using System;
using System.ComponentModel.DataAnnotations.Resources;
using System.Text.RegularExpressions;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class PhoneAttribute : DataTypeAttribute {
// see unit tests for examples
private static Regex _regex = CreateRegEx();
private const string _additionalPhoneNumberCharacters = "-.()";
public PhoneAttribute()
: base(DataType.PhoneNumber) {
// 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) {
if (value == null) {
return true;
}
string valueAsString = value as string;
// 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;
}
}
}
|