public PhoneRegex GetPatternForRegex(String regex) { lock (this) { Entry e = null; if (!cache_.TryGetValue(regex, out e)) { // Insert new node var r = new PhoneRegex(regex); var n = lru_.AddFirst(regex); cache_[regex] = e = new Entry { Regex = r, Node = n }; // Check cache size if (lru_.Count > size_) { var o = lru_.Last.Value; cache_.Remove(o); lru_.RemoveLast(); } } else { if (e.Node != lru_.First) { lru_.Remove(e.Node); lru_.AddFirst(e.Node); } } return e.Regex; } }
private bool MatchesEmergencyNumberHelper(String number, String regionCode, bool allowPrefixMatch) { number = PhoneNumberUtil.ExtractPossibleNumber(number); if (PhoneNumberUtil.PlusCharsPattern.MatchBeginning(number).Success) { // Returns false if the number starts with a plus sign. We don't believe dialing the country // code before emergency numbers (e.g. +1911) works, but later, if that proves to work, we can // add additional logic here to handle it. return(false); } var metadata = phoneUtil.GetMetadataForRegion(regionCode); if (metadata == null || !metadata.HasEmergency) { return(false); } var emergencyNumberPattern = new PhoneRegex(metadata.Emergency.NationalNumberPattern); var normalizedNumber = PhoneNumberUtil.NormalizeDigitsOnly(number); // In Brazil, it is impossible to append additional digits to an emergency number to dial the // number. return((!allowPrefixMatch || regionCode.Equals("BR")) ? emergencyNumberPattern.MatchAll(normalizedNumber).Success : emergencyNumberPattern.MatchBeginning(normalizedNumber).Success); }
public PhoneRegex GetPatternForRegex(string regex) { lock (regexLock) { Entry e; if (!cache.TryGetValue(regex, out e)) { // Insert new node var r = new PhoneRegex(regex); var n = lru.AddFirst(regex); cache[regex] = e = new Entry { Regex = r, Node = n }; // Check cache size if (lru.Count > size) { var o = lru.Last.Value; cache.Remove(o); lru.RemoveLast(); } } else { if (e.Node != lru.First) { lru.Remove(e.Node); lru.AddFirst(e.Node); } } return(e.Regex); } }
static PhoneNumberMatcher() { /* Builds the MATCHING_BRACKETS and PATTERN regular expressions. The building blocks below exist * to make the pattern more easily understood. */ String openingParens = "(\\[\uFF08\uFF3B"; String closingParens = ")\\]\uFF09\uFF3D"; String nonParens = "[^" + openingParens + closingParens + "]"; /* Limit on the number of pairs of brackets in a phone number. */ String bracketPairLimit = Limit(0, 3); /* * An opening bracket at the beginning may not be closed, but subsequent ones should be. It's * also possible that the leading bracket was dropped, so we shouldn't be surprised if we see a * closing bracket first. We limit the sets of brackets in a phone number to four. */ MATCHING_BRACKETS = new PhoneRegex( "(?:[" + openingParens + "])?" + "(?:" + nonParens + "+" + "[" + closingParens + "])?" + nonParens + "+" + "(?:[" + openingParens + "]" + nonParens + "+[" + closingParens + "])" + bracketPairLimit + nonParens + "*", RegexOptions.None); /* Limit on the number of leading (plus) characters. */ String leadLimit = Limit(0, 2); /* Limit on the number of consecutive punctuation characters. */ String punctuationLimit = Limit(0, 4); /* The maximum number of digits allowed in a digit-separated block. As we allow all digits in a * single block, set high enough to accommodate the entire national number and the international * country code. */ int digitBlockLimit = PhoneNumberUtil.MAX_LENGTH_FOR_NSN + PhoneNumberUtil.MAX_LENGTH_COUNTRY_CODE; /* Limit on the number of blocks separated by punctuation. Uses digitBlockLimit since some * formats use spaces to separate each digit. */ String blockLimit = Limit(0, digitBlockLimit); /* A punctuation sequence allowing white space. */ String punctuation = "[" + PhoneNumberUtil.VALID_PUNCTUATION + "]" + punctuationLimit; /* A digits block without punctuation. */ String digitSequence = "\\p{Nd}" + Limit(1, digitBlockLimit); String leadClassChars = openingParens + PhoneNumberUtil.PLUS_CHARS; String leadClass = "[" + leadClassChars + "]"; LEAD_CLASS = new PhoneRegex(leadClass, RegexOptions.None); GROUP_SEPARATOR = new PhoneRegex("\\p{Z}" + "[^" + leadClassChars + "\\p{Nd}]*"); /* Phone number pattern allowing optional punctuation. */ PATTERN = new Regex( "(?:" + leadClass + punctuation + ")" + leadLimit + digitSequence + "(?:" + punctuation + digitSequence + ")" + blockLimit + "(?:" + PhoneNumberUtil.EXTN_PATTERNS_FOR_MATCHING + ")?", PhoneNumberUtil.REGEX_FLAGS); }
static PhoneNumberMatcher() { /* Builds the MATCHING_BRACKETS and PATTERN regular expressions. The building blocks below exist * to make the pattern more easily understood. */ var openingParens = "(\\[\uFF08\uFF3B"; var closingParens = ")\\]\uFF09\uFF3D"; var nonParens = "[^" + openingParens + closingParens + "]"; /* Limit on the number of pairs of brackets in a phone number. */ var bracketPairLimit = Limit(0, 3); /* * An opening bracket at the beginning may not be closed, but subsequent ones should be. It's * also possible that the leading bracket was dropped, so we shouldn't be surprised if we see a * closing bracket first. We limit the sets of brackets in a phone number to four. */ MatchingBrackets = new PhoneRegex( "(?:[" + openingParens + "])?" + "(?:" + nonParens + "+" + "[" + closingParens + "])?" + nonParens + "+" + "(?:[" + openingParens + "]" + nonParens + "+[" + closingParens + "])" + bracketPairLimit + nonParens + "*", InternalRegexOptions.Default); /* Limit on the number of leading (plus) characters. */ var leadLimit = Limit(0, 2); /* Limit on the number of consecutive punctuation characters. */ var punctuationLimit = Limit(0, 4); /* The maximum number of digits allowed in a digit-separated block. As we allow all digits in a * single block, set high enough to accommodate the entire national number and the international * country code. */ var digitBlockLimit = PhoneNumberUtil.MaxLengthForNsn + PhoneNumberUtil.MaxLengthCountryCode; /* Limit on the number of blocks separated by punctuation. Uses digitBlockLimit since some * formats use spaces to separate each digit. */ var blockLimit = Limit(0, digitBlockLimit); /* A punctuation sequence allowing white space. */ var punctuation = "[" + PhoneNumberUtil.ValidPunctuation + "]" + punctuationLimit; /* A digits block without punctuation. */ var digitSequence = "\\p{Nd}" + Limit(1, digitBlockLimit); var leadClassChars = openingParens + PhoneNumberUtil.PlusChars; var leadClass = "[" + leadClassChars + "]"; LeadClass = new PhoneRegex(leadClass, InternalRegexOptions.Default); GroupSeparator = new PhoneRegex("\\p{Z}" + "[^" + leadClassChars + "\\p{Nd}]*"); /* Phone number pattern allowing optional punctuation. */ Pattern = new Regex( "(?:" + leadClass + punctuation + ")" + leadLimit + digitSequence + "(?:" + punctuation + digitSequence + ")" + blockLimit + "(?:" + PhoneNumberUtil.ExtnPatternsForMatching + ")?", PhoneNumberUtil.RegexFlags); }
public PhoneRegex GetPatternForRegex(string regex) { lock (this) { Entry e = null; if (cache_.TryGetValue(regex, out e)) { if (e.Node == lru_.First) { return(e.Regex); } lru_.Remove(e.Node); lru_.AddFirst(e.Node); } else { // Insert new node var r = new PhoneRegex(regex); var n = lru_.AddFirst(regex); cache_[regex] = e = new Entry { Regex = r, Node = n }; // Check cache size if (lru_.Count <= size_) { return(e.Regex); } var o = lru_.Last.Value; cache_.Remove(o); lru_.RemoveLast(); } return(e.Regex); } }
private bool MatchesEmergencyNumberHelper(String number, String regionCode, bool allowPrefixMatch) { number = PhoneNumberUtil.ExtractPossibleNumber(number); if (PhoneNumberUtil.PLUS_CHARS_PATTERN.MatchBeginning(number).Success) { // Returns false if the number starts with a plus sign. We don't believe dialing the country // code before emergency numbers (e.g. +1911) works, but later, if that proves to work, we can // add additional logic here to handle it. return false; } PhoneMetadata metadata = phoneUtil.GetMetadataForRegion(regionCode); if (metadata == null || !metadata.HasEmergency) { return false; } var emergencyNumberPattern = new PhoneRegex(metadata.Emergency.NationalNumberPattern); String normalizedNumber = PhoneNumberUtil.NormalizeDigitsOnly(number); // In Brazil, it is impossible to append additional digits to an emergency number to dial the // number. return (!allowPrefixMatch || regionCode.Equals("BR")) ? emergencyNumberPattern.MatchAll(normalizedNumber).Success : emergencyNumberPattern.MatchBeginning(normalizedNumber).Success; }
/** * Helper method to check a number against a particular pattern and determine whether it matches, * or is too short or too long. Currently, if a number pattern suggests that numbers of length 7 * and 10 are possible, and a number in between these possible lengths is entered, such as of * length 8, this will return TOO_LONG. */ private ValidationResult TestNumberLengthAgainstPattern(PhoneRegex numberPattern, String number) { if (numberPattern.MatchAll(number).Success) return ValidationResult.IS_POSSIBLE; if (numberPattern.MatchBeginning(number).Success) return ValidationResult.TOO_LONG; return ValidationResult.TOO_SHORT; }
/** * Strips the IDD from the start of the number if present. Helper function used by * maybeStripInternationalPrefixAndNormalize. */ private bool ParsePrefixAsIdd(PhoneRegex iddPattern, StringBuilder number) { var m = iddPattern.MatchBeginning(number.ToString()); if (m.Success) { int matchEnd = m.Index + m.Length; // Only strip this if the first digit after the match is not a 0, since country calling codes // cannot begin with 0. var digitMatcher = CAPTURING_DIGIT_PATTERN.Match(number.ToString().Substring(matchEnd)); //XXX: ToString if (digitMatcher.Success) { String normalizedGroup = NormalizeDigitsOnly(digitMatcher.Groups[1].Value); if (normalizedGroup == "0") return false; } number.Remove(0, matchEnd); return true; } return false; }
static PhoneNumberUtil() { thisLock = new Object(); // Simple ASCII digits map used to populate ALPHA_PHONE_MAPPINGS and // ALL_PLUS_NUMBER_GROUPING_SYMBOLS. var asciiDigitMappings = new Dictionary<char, char> { {'0', '0'}, {'1', '1'}, {'2', '2'}, {'3', '3'}, {'4', '4'}, {'5', '5'}, {'6', '6'}, {'7', '7'}, {'8', '8'}, {'9', '9'}, }; var alphaMap = new Dictionary<char, char>(); alphaMap['A'] = '2'; alphaMap['B'] = '2'; alphaMap['C'] = '2'; alphaMap['D'] = '3'; alphaMap['E'] = '3'; alphaMap['F'] = '3'; alphaMap['G'] = '4'; alphaMap['H'] = '4'; alphaMap['I'] = '4'; alphaMap['J'] = '5'; alphaMap['K'] = '5'; alphaMap['L'] = '5'; alphaMap['M'] = '6'; alphaMap['N'] = '6'; alphaMap['O'] = '6'; alphaMap['P'] = '7'; alphaMap['Q'] = '7'; alphaMap['R'] = '7'; alphaMap['S'] = '7'; alphaMap['T'] = '8'; alphaMap['U'] = '8'; alphaMap['V'] = '8'; alphaMap['W'] = '9'; alphaMap['X'] = '9'; alphaMap['Y'] = '9'; alphaMap['Z'] = '9'; ALPHA_MAPPINGS = alphaMap; var combinedMap = new Dictionary<char, char>(ALPHA_MAPPINGS); foreach (var k in asciiDigitMappings) combinedMap[k.Key] = k.Value; ALPHA_PHONE_MAPPINGS = combinedMap; var diallableCharMap = new Dictionary<char, char>(); foreach (var k in asciiDigitMappings) diallableCharMap[k.Key] = k.Value; diallableCharMap[PLUS_SIGN] = PLUS_SIGN; diallableCharMap['*'] = '*'; DIALLABLE_CHAR_MAPPINGS = diallableCharMap; var allPlusNumberGroupings = new Dictionary<char, char>(); // Put (lower letter -> upper letter) and (upper letter -> upper letter) mappings. foreach (var c in ALPHA_MAPPINGS.Keys) { allPlusNumberGroupings[char.ToLowerInvariant(c)] = c; allPlusNumberGroupings[c] = c; } foreach (var k in asciiDigitMappings) allPlusNumberGroupings[k.Key] = k.Value; // Put grouping symbols. allPlusNumberGroupings['-'] = '-'; allPlusNumberGroupings['\uFF0D'] = '-'; allPlusNumberGroupings['\u2010'] = '-'; allPlusNumberGroupings['\u2011'] = '-'; allPlusNumberGroupings['\u2012'] = '-'; allPlusNumberGroupings['\u2013'] = '-'; allPlusNumberGroupings['\u2014'] = '-'; allPlusNumberGroupings['\u2015'] = '-'; allPlusNumberGroupings['\u2212'] = '-'; allPlusNumberGroupings['/'] = '/'; allPlusNumberGroupings['\uFF0F'] = '/'; allPlusNumberGroupings[' '] = ' '; allPlusNumberGroupings['\u3000'] = ' '; allPlusNumberGroupings['\u2060'] = ' '; allPlusNumberGroupings['.'] = '.'; allPlusNumberGroupings['\uFF0E'] = '.'; ALL_PLUS_NUMBER_GROUPING_SYMBOLS = allPlusNumberGroupings; // We accept alpha characters in phone numbers, ASCII only, upper and lower case. VALID_ALPHA = String.Join("", ALPHA_MAPPINGS.Keys.Where(c => !"[, \\[\\]]".Contains(c.ToString())).ToList().ConvertAll(c => c.ToString()).ToArray()) + String.Join("", ALPHA_MAPPINGS.Keys.Where(c => !"[, \\[\\]]".Contains(c.ToString())).ToList().ConvertAll(c => c.ToString()).ToArray()).ToLower(); CAPTURING_DIGIT_PATTERN = new Regex("(" + DIGITS + ")", RegexOptions.None); VALID_START_CHAR = "[" + PLUS_CHARS + DIGITS + "]"; VALID_START_CHAR_PATTERN = new PhoneRegex(VALID_START_CHAR, RegexOptions.None); CAPTURING_EXTN_DIGITS = "(" + DIGITS + "{1,7})"; VALID_PHONE_NUMBER = DIGITS + "{" + MIN_LENGTH_FOR_NSN + "}" + "|" + "[" + PLUS_CHARS + "]*(?:[" + VALID_PUNCTUATION + STAR_SIGN + "]*" + DIGITS + "){3,}[" + VALID_PUNCTUATION + STAR_SIGN + VALID_ALPHA + DIGITS + "]*"; // One-character symbols that can be used to indicate an extension. String singleExtnSymbolsForMatching = "x\uFF58#\uFF03~\uFF5E"; // For parsing, we are slightly more lenient in our interpretation than for matching. Here we // allow a "comma" as a possible extension indicator. When matching, this is hardly ever used to // indicate this. String singleExtnSymbolsForParsing = "," + singleExtnSymbolsForMatching; EXTN_PATTERNS_FOR_PARSING = CreateExtnPattern(singleExtnSymbolsForParsing); EXTN_PATTERNS_FOR_MATCHING = CreateExtnPattern(singleExtnSymbolsForMatching); EXTN_PATTERN = new Regex("(?:" + EXTN_PATTERNS_FOR_PARSING + ")$", REGEX_FLAGS); VALID_PHONE_NUMBER_PATTERN = new PhoneRegex(VALID_PHONE_NUMBER + "(?:" + EXTN_PATTERNS_FOR_PARSING + ")?", REGEX_FLAGS); }
/** * Returns true if the number matches the short code number format for the given region. * * This method takes into account cases where the number might contain formatting, but doesn't * allow additional digits to be appended. * * @param number the phone number to test * @param regionCode the region where the phone number is being dialed * @return if the number matches the short code number format for the given region. */ public bool IsShortcodeNumber(string number, string regionCode) { var phoneMetadataForRegion = phoneUtil.GetMetadataForRegion(regionCode); if (phoneMetadataForRegion == null || !phoneMetadataForRegion.HasShortCode) { // NOTE: We should also probably do this when phoneMetadataForRegion.ShortCode.NationalNumberPattern.Equals("NA") // I think there is a bug where PhoneNumbers.BuildMetadataFromXml.LoadGeneralDesc always calls metadata.SetShortCode(), // which always sets HasShortCode to true, even in the cases where PhoneNumbers.BuildMetadataFromXml.ProcessPhoneNumberDescElement // returns "NA" (which happens when the territory in PhoneNumberMetaData.xml does not contain a shortCode definition/node) return false; } var shortCodeNumberPattern = new PhoneRegex(phoneMetadataForRegion.ShortCode.NationalNumberPattern); var normalizedNumber = PhoneNumberUtil.NormalizeDigitsOnly(number); return shortCodeNumberPattern.MatchAll(normalizedNumber).Success; }