Inheritance: System.Text.RegularExpressions.Regex
 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;
     }
 }
Example #2
0
        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);
        }
Example #5
0
        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);
        }
Example #6
0
        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;
        }