/**
         * Gets the expected cost category of a short number (however, nothing is implied about its
         * validity). If it is important that the number is valid, then its validity must first be checked
         * using {@link isValidShortNumber}. Note that emergency numbers are always considered toll-free.
         * Example usage:
         * <pre>{@code
         * PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
         * ShortNumberInfo shortInfo = ShortNumberInfo.getInstance();
         * PhoneNumber number = phoneUtil.parse("110", "FR");
         * if (shortInfo.isValidShortNumber(number)) {
         *   ShortNumberInfo.ShortNumberCost cost = shortInfo.getExpectedCost(number);
         *   // Do something with the cost information here.
         * }}</pre>
         *
         * @param number the short number for which we want to know the expected cost category
         * @return the expected cost category of the short number. Returns UNKNOWN_COST if the number does
         *     not match a cost category. Note that an invalid number may match any cost category.
         */
        public ShortNumberCost getExpectedCost(PhoneNumber number)
        {
            List <String> regionCodes = phoneUtil.getRegionCodesForCountryCode(number.getCountryCode());
            String        regionCode  = getRegionCodeForShortNumberFromRegionList(number, regionCodes);

            // Note that regionCode may be null, in which case phoneMetadata will also be null.
            PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);

            if (phoneMetadata == null)
            {
                return(ShortNumberCost.UNKNOWN_COST);
            }
            String nationalNumber = phoneUtil.getNationalSignificantNumber(number);

            // The cost categories are tested in order of decreasing expense, since if for some reason the
            // patterns overlap the most expensive matching cost category should be returned.
            if (phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getPremiumRate()))
            {
                return(ShortNumberCost.PREMIUM_RATE);
            }
            if (phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getStandardRate()))
            {
                return(ShortNumberCost.STANDARD_RATE);
            }
            if (phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getTollFree()))
            {
                return(ShortNumberCost.TOLL_FREE);
            }
            if (isEmergencyNumber(nationalNumber, regionCode))
            {
                // Emergency numbers are implicitly toll-free.
                return(ShortNumberCost.TOLL_FREE);
            }
            return(ShortNumberCost.UNKNOWN_COST);
        }
Пример #2
0
        /**
         * Extracts the country calling code from the beginning of nationalNumber to
         * prefixBeforeNationalNumber when they are available, and places the remaining input into
         * nationalNumber.
         *
         * @return  true when a valid country calling code can be found.
         */
        private boolean attemptToExtractCountryCallingCode()
        {
            if (nationalNumber.length() == 0)
            {
                return(false);
            }
            StringBuilder numberWithoutCountryCallingCode = new StringBuilder();
            int           countryCode = phoneUtil.extractCountryCode(nationalNumber, numberWithoutCountryCallingCode);

            if (countryCode == 0)
            {
                return(false);
            }
            nationalNumber.setLength(0);
            nationalNumber.append(numberWithoutCountryCallingCode);
            String newRegionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);

            if (PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY.equals(newRegionCode))
            {
                currentMetadata = phoneUtil.getMetadataForNonGeographicalRegion(countryCode);
            }
            else if (!newRegionCode.equals(defaultCountry))
            {
                currentMetadata = getMetadataForRegion(newRegionCode);
            }
            String countryCodeString = Integer.toString(countryCode);

            prefixBeforeNationalNumber.append(countryCodeString).append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
            return(true);
        }
        private boolean matchesEmergencyNumberHelper(String number, String regionCode,
                                                     boolean allowPrefixMatch)
        {
            number = PhoneNumberUtil.extractPossibleNumber(number);
            if (PhoneNumberUtil.PLUS_CHARS_PATTERN.matcher(number).lookingAt())
            {
                // 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 = MetadataManager.getShortNumberMetadataForRegion(regionCode);

            if (metadata == null || !metadata.hasEmergency())
            {
                return(false);
            }
            Pattern emergencyNumberPattern =
                Pattern.compile(metadata.getEmergency().getNationalNumberPattern());
            String normalizedNumber = PhoneNumberUtil.normalizeDigitsOnly(number);

            // In Brazil and Chile, emergency numbers don't work when additional digits are appended.
            return((!allowPrefixMatch || regionCode == "BR" || regionCode == "CL")
        ? emergencyNumberPattern.matcher(normalizedNumber).matches()
        : emergencyNumberPattern.matcher(normalizedNumber).lookingAt());
        }
Пример #4
0
        internal static boolean checkNumberGroupingIsValid(
            PhoneNumber number, String candidate, PhoneNumberUtil util, NumberGroupingChecker checker)
        {
            // TODO: Evaluate how this works for other locales (testing has been limited to NANPA regions)
            // and optimise if necessary.
            StringBuilder normalizedCandidate =
                PhoneNumberUtil.normalizeDigits(candidate, true /* keep non-digits */);

            String[] formattedNumberGroups = getNationalNumberGroups(util, number, null);
            if (checker.checkGroups(util, number, normalizedCandidate, formattedNumberGroups))
            {
                return(true);
            }
            // If this didn't pass, see if there are any alternate formats, and try them instead.
            PhoneMetadata alternateFormats =
                MetadataManager.getAlternateFormatsForCountry(number.getCountryCode());

            if (alternateFormats != null)
            {
                foreach (NumberFormat alternateFormat in alternateFormats.numberFormats())
                {
                    formattedNumberGroups = getNationalNumberGroups(util, number, alternateFormat);
                    if (checker.checkGroups(util, number, normalizedCandidate, formattedNumberGroups))
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
        /**
         * Gets a valid short number for the specified cost category.
         *
         * @param regionCode the region for which an example short number is needed
         * @param cost the cost category of number that is needed
         * @return a valid short number for the specified region and cost category. Returns an empty
         *     string when the metadata does not contain such information, or the cost is UNKNOWN_COST.
         */
        // @VisibleForTesting
        internal String getExampleShortNumberForCost(String regionCode, ShortNumberCost cost)
        {
            PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);

            if (phoneMetadata == null)
            {
                return("");
            }
            PhoneNumberDesc desc = null;

            switch (cost)
            {
            case ShortNumberCost.TOLL_FREE:
                desc = phoneMetadata.getTollFree();
                break;

            case ShortNumberCost.STANDARD_RATE:
                desc = phoneMetadata.getStandardRate();
                break;

            case ShortNumberCost.PREMIUM_RATE:
                desc = phoneMetadata.getPremiumRate();
                break;

            default:
                // UNKNOWN_COST numbers are computed by the process of elimination from the other cost
                // categories.
                break;
            }
            if (desc != null && desc.hasExampleNumber())
            {
                return(desc.getExampleNumber());
            }
            return("");
        }
        // Helper method to get the region code for a given phone number, from a list of possible region
        // codes. If the list contains more than one region, the first region for which the number is
        // valid is returned.
        private String getRegionCodeForShortNumberFromRegionList(PhoneNumber number,
                                                                 List <String> regionCodes)
        {
            if (regionCodes.size() == 0)
            {
                return(null);
            }
            else if (regionCodes.size() == 1)
            {
                return(regionCodes.get(0));
            }
            String nationalNumber = phoneUtil.getNationalSignificantNumber(number);

            foreach (String regionCode in regionCodes)
            {
                PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);
                if (phoneMetadata != null &&
                    phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getShortCode()))
                {
                    // The number is valid for this region.
                    return(regionCode);
                }
            }
            return(null);
        }
        /**
         * Tests whether a short number matches a valid pattern. Note that this doesn't verify the number
         * is actually in use, which is impossible to tell by just looking at the number itself.
         *
         * @param shortNumber the short number to check as a string
         * @param regionDialingFrom the region from which the number is dialed
         * @return whether the short number matches a valid pattern
         */
        public boolean isValidShortNumber(String shortNumber, String regionDialingFrom)
        {
            PhoneMetadata phoneMetadata =
                MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);

            if (phoneMetadata == null)
            {
                return(false);
            }
            PhoneNumberDesc generalDesc = phoneMetadata.getGeneralDesc();

            if (!generalDesc.hasNationalNumberPattern() ||
                !phoneUtil.isNumberMatchingDesc(shortNumber, generalDesc))
            {
                return(false);
            }
            PhoneNumberDesc shortNumberDesc = phoneMetadata.getShortCode();

            if (!shortNumberDesc.hasNationalNumberPattern())
            {
                logger.log(Level.WARNING, "No short code national number pattern found for region: " +
                           regionDialingFrom);
                return(false);
            }
            return(phoneUtil.isNumberMatchingDesc(shortNumber, shortNumberDesc));
        }
        [TestMethod] public void testShortNumberMetadataContainsData()
        {
            // We should have some data for France.
            PhoneMetadata franceShortNumberMetadata = MetadataManager.getShortNumberMetadataForRegion("FR");

            assertNotNull(franceShortNumberMetadata);
            assertTrue(franceShortNumberMetadata.hasShortCode());
        }
        [TestMethod] public void testAlternateFormatsContainsData()
        {
            // We should have some data for Germany.
            PhoneMetadata germanyAlternateFormats = MetadataManager.getAlternateFormatsForCountry(49);

            assertNotNull(germanyAlternateFormats);
            assertTrue(germanyAlternateFormats.numberFormats().size() > 0);
        }
Пример #10
0
        /**
         * Given a valid short number, determines whether it is carrier-specific (however, nothing is
         * implied about its validity). If it is important that the number is valid, then its validity
         * must first be checked using {@link isValidShortNumber}.
         *
         * @param number the valid short number to check
         * @return whether the short number is carrier-specific (assuming the input was a valid short
         *     number).
         */
        public boolean isCarrierSpecific(PhoneNumber number)
        {
            List <String> regionCodes    = phoneUtil.getRegionCodesForCountryCode(number.getCountryCode());
            String        regionCode     = getRegionCodeForShortNumberFromRegionList(number, regionCodes);
            String        nationalNumber = phoneUtil.getNationalSignificantNumber(number);
            PhoneMetadata phoneMetadata  = MetadataManager.getShortNumberMetadataForRegion(regionCode);

            return((phoneMetadata != null) &&
                   (phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getCarrierSpecific())));
        }
Пример #11
0
        /**
         * Check whether a short number is a possible number, given the number in the form of a string,
         * and the region where the number is dialed from. This provides a more lenient check than
         * {@link #isValidShortNumber}.
         *
         * @param shortNumber the short number to check as a string
         * @param regionDialingFrom the region from which the number is dialed
         * @return whether the number is a possible short number
         */
        public boolean isPossibleShortNumber(String shortNumber, String regionDialingFrom)
        {
            PhoneMetadata phoneMetadata =
                MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);

            if (phoneMetadata == null)
            {
                return(false);
            }
            PhoneNumberDesc generalDesc = phoneMetadata.getGeneralDesc();

            return(phoneUtil.isNumberPossibleForDesc(shortNumber, generalDesc));
        }
Пример #12
0
        // The metadata needed by this class is the same for all regions sharing the same country calling
        // code. Therefore, we return the metadata for "main" region for this country calling code.
        private PhoneMetadata getMetadataForRegion(String regionCode)
        {
            int           countryCallingCode = phoneUtil.getCountryCodeForRegion(regionCode);
            String        mainCountry        = phoneUtil.getRegionCodeForCountryCode(countryCallingCode);
            PhoneMetadata metadata           = phoneUtil.getMetadataForRegion(mainCountry);

            if (metadata != null)
            {
                return(metadata);
            }
            // Set to a default instance of the metadata. This allows us to function with an incorrect
            // region code, even if formatting only works for numbers specified with "+".
            return(EMPTY_METADATA);
        }
Пример #13
0
        /**
         * Gets a valid short number for the specified region.
         *
         * @param regionCode the region for which an example short number is needed
         * @return a valid short number for the specified region. Returns an empty string when the
         *     metadata does not contain such information.
         */
        // @VisibleForTesting
        internal String getExampleShortNumber(String regionCode)
        {
            PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);

            if (phoneMetadata == null)
            {
                return("");
            }
            PhoneNumberDesc desc = phoneMetadata.getShortCode();

            if (desc.hasExampleNumber())
            {
                return(desc.getExampleNumber());
            }
            return("");
        }
Пример #14
0
        internal static boolean isNationalPrefixPresentIfRequired(PhoneNumber number, PhoneNumberUtil util)
        {
            // First, check how we deduced the country code. If it was written in international format, then
            // the national prefix is not required.
            if (number.getCountryCodeSource() != CountryCodeSource.FROM_DEFAULT_COUNTRY)
            {
                return(true);
            }
            String phoneNumberRegion =
                util.getRegionCodeForCountryCode(number.getCountryCode());
            PhoneMetadata metadata = util.getMetadataForRegion(phoneNumberRegion);

            if (metadata == null)
            {
                return(true);
            }
            // Check if a national prefix should be present when formatting this number.
            String       nationalNumber = util.getNationalSignificantNumber(number);
            NumberFormat formatRule     =
                util.chooseFormattingPatternForNumber(metadata.numberFormats(), nationalNumber);

            // To do this, we check that a national prefix formatting rule was present and that it wasn't
            // just the first-group symbol ($1) with punctuation.
            if ((formatRule != null) && formatRule.getNationalPrefixFormattingRule().length() > 0)
            {
                if (formatRule.isNationalPrefixOptionalWhenFormatting())
                {
                    // The national-prefix is optional in these cases, so we don't need to check if it was
                    // present.
                    return(true);
                }
                if (PhoneNumberUtil.formattingRuleHasFirstGroupOnly(
                        formatRule.getNationalPrefixFormattingRule()))
                {
                    // National Prefix not needed for this number.
                    return(true);
                }
                // Normalize the remainder.
                String        rawInputCopy = PhoneNumberUtil.normalizeDigitsOnly(number.getRawInput());
                StringBuilder rawInput     = new StringBuilder(rawInputCopy);
                // Check if we found a national prefix and/or carrier code at the start of the raw input, and
                // return the result.
                return(util.maybeStripNationalPrefixAndCarrierCode(rawInput, metadata, null));
            }
            return(true);
        }
Пример #15
0
 /**
  * Clears the internal state of the formatter, so it can be reused.
  */
 public void clear()
 {
     currentOutput = "";
     accruedInput.setLength(0);
     accruedInputWithoutFormatting.setLength(0);
     formattingTemplate.setLength(0);
     lastMatchPosition        = 0;
     currentFormattingPattern = "";
     prefixBeforeNationalNumber.setLength(0);
     nationalPrefixExtracted = "";
     nationalNumber.setLength(0);
     ableToFormat                  = true;
     inputHasFormatting            = false;
     positionToRemember            = 0;
     originalPosition              = 0;
     isCompleteNumber              = false;
     isExpectingCountryCallingCode = false;
     possibleFormats.clear();
     shouldAddSpaceAfterNationalPrefix = false;
     if (!currentMetadata.equals(defaultMetadata))
     {
         currentMetadata = getMetadataForRegion(defaultCountry);
     }
 }
        [TestMethod] public void testShortNumberMetadataFailsGracefully()
        {
            PhoneMetadata noShortNumberMetadata = MetadataManager.getShortNumberMetadataForRegion("XXX");

            assertNull(noShortNumberMetadata);
        }
        [TestMethod] public void testAlternateFormatsFailsGracefully()
        {
            PhoneMetadata noAlternateFormats = MetadataManager.getAlternateFormatsForCountry(999);

            assertNull(noAlternateFormats);
        }
 public void testMaybeStripNationalPrefix()
 {
     PhoneMetadata metadata = new PhoneMetadata();
     metadata.setNationalPrefixForParsing("34");
     metadata.setGeneralDesc(new PhoneNumberDesc().setNationalNumberPattern("\\d{4,8}"));
     StringBuilder numberToStrip = new StringBuilder("34356778");
     String strippedNumber = "356778";
     assertTrue(phoneUtil.maybeStripNationalPrefixAndCarrierCode(numberToStrip, metadata, null));
     assertEquals("Should have had national prefix stripped.",
          strippedNumber, numberToStrip.toString());
     // Retry stripping - now the number should not start with the national prefix, so no more
     // stripping should occur.
     assertFalse(phoneUtil.maybeStripNationalPrefixAndCarrierCode(numberToStrip, metadata, null));
     assertEquals("Should have had no change - no national prefix present.",
          strippedNumber, numberToStrip.toString());
     // Some countries have no national prefix. Repeat test with none specified.
     metadata.setNationalPrefixForParsing("");
     assertFalse(phoneUtil.maybeStripNationalPrefixAndCarrierCode(numberToStrip, metadata, null));
     assertEquals("Should not strip anything with empty national prefix.",
          strippedNumber, numberToStrip.toString());
     // If the resultant number doesn't match the national rule, it shouldn't be stripped.
     metadata.setNationalPrefixForParsing("3");
     numberToStrip = new StringBuilder("3123");
     strippedNumber = "3123";
     assertFalse(phoneUtil.maybeStripNationalPrefixAndCarrierCode(numberToStrip, metadata, null));
     assertEquals("Should have had no change - after stripping, it wouldn't have matched " +
          "the national rule.",
          strippedNumber, numberToStrip.toString());
     // Test extracting carrier selection code.
     metadata.setNationalPrefixForParsing("0(81)?");
     numberToStrip = new StringBuilder("08122123456");
     strippedNumber = "22123456";
     StringBuilder carrierCode = new StringBuilder();
     assertTrue(phoneUtil.maybeStripNationalPrefixAndCarrierCode(
     numberToStrip, metadata, carrierCode));
     assertEquals("81", carrierCode.toString());
     assertEquals("Should have had national prefix and carrier code stripped.",
          strippedNumber, numberToStrip.toString());
     // If there was a transform rule, check it was applied.
     metadata.setNationalPrefixTransformRule("5$15");
     // Note that a capturing group is present here.
     metadata.setNationalPrefixForParsing("0(\\d{2})");
     numberToStrip = new StringBuilder("031123");
     String transformedNumber = "5315123";
     assertTrue(phoneUtil.maybeStripNationalPrefixAndCarrierCode(numberToStrip, metadata, null));
     assertEquals("Should transform the 031 to a 5315.",
          transformedNumber, numberToStrip.toString());
 }
 /**
    * Constructs an as-you-type formatter. Should be obtained from {@link
    * PhoneNumberUtil#getAsYouTypeFormatter}.
    *
    * @param regionCode  the country/region where the phone number is being entered
    */
 internal AsYouTypeFormatter(String regionCode)
 {
     defaultCountry = regionCode;
     currentMetadata = getMetadataForRegion(defaultCountry);
     defaultMetadata = currentMetadata;
 }
Пример #20
0
 /**
  * Constructs an as-you-type formatter. Should be obtained from {@link
  * PhoneNumberUtil#getAsYouTypeFormatter}.
  *
  * @param regionCode  the country/region where the phone number is being entered
  */
 internal AsYouTypeFormatter(String regionCode)
 {
     defaultCountry  = regionCode;
     currentMetadata = getMetadataForRegion(defaultCountry);
     defaultMetadata = currentMetadata;
 }
 /**
    * Extracts the country calling code from the beginning of nationalNumber to
    * prefixBeforeNationalNumber when they are available, and places the remaining input into
    * nationalNumber.
    *
    * @return  true when a valid country calling code can be found.
    */
 private boolean attemptToExtractCountryCallingCode()
 {
     if (nationalNumber.length() == 0) {
       return false;
     }
     StringBuilder numberWithoutCountryCallingCode = new StringBuilder();
     int countryCode = phoneUtil.extractCountryCode(nationalNumber, numberWithoutCountryCallingCode);
     if (countryCode == 0) {
       return false;
     }
     nationalNumber.setLength(0);
     nationalNumber.append(numberWithoutCountryCallingCode);
     String newRegionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
     if (PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY.equals(newRegionCode)) {
       currentMetadata = phoneUtil.getMetadataForNonGeographicalRegion(countryCode);
     } else if (!newRegionCode.equals(defaultCountry)) {
       currentMetadata = getMetadataForRegion(newRegionCode);
     }
     String countryCodeString = Integer.toString(countryCode);
     prefixBeforeNationalNumber.append(countryCodeString).append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
     return true;
 }
 /**
    * Clears the internal state of the formatter, so it can be reused.
    */
 public void clear()
 {
     currentOutput = "";
     accruedInput.setLength(0);
     accruedInputWithoutFormatting.setLength(0);
     formattingTemplate.setLength(0);
     lastMatchPosition = 0;
     currentFormattingPattern = "";
     prefixBeforeNationalNumber.setLength(0);
     nationalPrefixExtracted = "";
     nationalNumber.setLength(0);
     ableToFormat = true;
     inputHasFormatting = false;
     positionToRemember = 0;
     originalPosition = 0;
     isCompleteNumber = false;
     isExpectingCountryCallingCode = false;
     possibleFormats.clear();
     shouldAddSpaceAfterNationalPrefix = false;
     if (!currentMetadata.equals(defaultMetadata)) {
       currentMetadata = getMetadataForRegion(defaultCountry);
     }
 }