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()); }
/** * 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); }
/** * 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); }
/** * 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()); }
/** * 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()))); }
/** * 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)); }
/** * 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(""); }
[TestMethod] public void testEmergency() { int wrongTypeCounter = 0; foreach (String regionCode in shortNumberInfo.getSupportedRegions()) { if (regionCode == RegionCode.PG) { // The only short number for Papua New Guinea is 000, which fails the test, since the // national prefix is 0. This needs to be fixed. continue; } PhoneNumberDesc desc = MetadataManager.getShortNumberMetadataForRegion(regionCode).getEmergency(); if (desc.hasExampleNumber()) { String exampleNumber = desc.getExampleNumber(); if (!exampleNumber.matches(desc.getPossibleNumberPattern()) || !shortNumberInfo.isEmergencyNumber(exampleNumber, regionCode)) { wrongTypeCounter++; LOGGER.log(Level.SEVERE, "Emergency example number test failed for " + regionCode); } else { PhoneNumber emergencyNumber = phoneNumberUtil.parse(exampleNumber, regionCode); if (shortNumberInfo.getExpectedCost(emergencyNumber) != ShortNumberInfo.ShortNumberCost.TOLL_FREE) { wrongTypeCounter++; LOGGER.log(Level.SEVERE, "Emergency example number not toll free for " + regionCode); } } } } assertEquals(0, wrongTypeCounter); }
[TestMethod] public void testShortNumberMetadataFailsGracefully() { PhoneMetadata noShortNumberMetadata = MetadataManager.getShortNumberMetadataForRegion("XXX"); assertNull(noShortNumberMetadata); }