import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.google.i18n.phonenumbers.geocoding.PrefixFileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.util.HashMap;
import java.util.Locale;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
/**
* An offline geocoder which provides geographical information related to a phone number.
private static PhoneNumberOfflineGeocoder instance = null;
private static final String MAPPING_DATA_DIRECTORY =
"/com/google/i18n/phonenumbers/geocoding/data/";
- private static final Logger LOGGER = Logger.getLogger(PhoneNumberOfflineGeocoder.class.getName());
-
+ private PrefixFileReader prefixFileReader = null;
+
private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
- private final String phonePrefixDataDirectory;
-
- // The mappingFileProvider knows for which combination of countryCallingCode and language a phone
- // prefix mapping file is available in the file system, so that a file can be loaded when needed.
- private MappingFileProvider mappingFileProvider = new MappingFileProvider();
-
- // A mapping from countryCallingCode_lang to the corresponding phone prefix map that has been
- // loaded.
- private Map<String, AreaCodeMap> availablePhonePrefixMaps = new HashMap<String, AreaCodeMap>();
// @VisibleForTesting
PhoneNumberOfflineGeocoder(String phonePrefixDataDirectory) {
- this.phonePrefixDataDirectory = phonePrefixDataDirectory;
- loadMappingFileProvider();
- }
-
- private void loadMappingFileProvider() {
- InputStream source =
- PhoneNumberOfflineGeocoder.class.getResourceAsStream(phonePrefixDataDirectory + "config");
- ObjectInputStream in = null;
- try {
- in = new ObjectInputStream(source);
- mappingFileProvider.readExternal(in);
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, e.toString());
- } finally {
- close(in);
- }
- }
-
- private AreaCodeMap getPhonePrefixDescriptions(
- int prefixMapKey, String language, String script, String region) {
- String fileName = mappingFileProvider.getFileName(prefixMapKey, language, script, region);
- if (fileName.length() == 0) {
- return null;
- }
- if (!availablePhonePrefixMaps.containsKey(fileName)) {
- loadAreaCodeMapFromFile(fileName);
- }
- return availablePhonePrefixMaps.get(fileName);
- }
-
- private void loadAreaCodeMapFromFile(String fileName) {
- InputStream source =
- PhoneNumberOfflineGeocoder.class.getResourceAsStream(phonePrefixDataDirectory + fileName);
- ObjectInputStream in = null;
- try {
- in = new ObjectInputStream(source);
- AreaCodeMap map = new AreaCodeMap();
- map.readExternal(in);
- availablePhonePrefixMaps.put(fileName, map);
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, e.toString());
- } finally {
- close(in);
- }
- }
-
- private static void close(InputStream in) {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, e.toString());
- }
- }
+ prefixFileReader = new PrefixFileReader(phonePrefixDataDirectory);
}
/**
String scriptStr = ""; // No script is specified
String regionStr = languageCode.getCountry();
- String areaDescription =
- getAreaDescriptionForNumber(number, langStr, scriptStr, regionStr);
+ String areaDescription;
+ String mobileToken = PhoneNumberUtil.getCountryMobileToken(number.getCountryCode());
+ String nationalNumber = phoneUtil.getNationalSignificantNumber(number);
+ if (!mobileToken.equals("") && nationalNumber.startsWith(mobileToken)) {
+ // In some countries, eg. Argentina, mobile numbers have a mobile token before the national
+ // destination code, this should be removed before geocoding.
+ nationalNumber = nationalNumber.substring(mobileToken.length());
+ PhoneNumber copiedNumber = new PhoneNumber();
+ copiedNumber.setCountryCode(number.getCountryCode());
+ copiedNumber.setNationalNumber(Long.parseLong(nationalNumber));
+ if (nationalNumber.startsWith("0")) {
+ copiedNumber.setItalianLeadingZero(true);
+ }
+ areaDescription = prefixFileReader.getDescriptionForNumber(copiedNumber, langStr, scriptStr,
+ regionStr);
+ } else {
+ areaDescription = prefixFileReader.getDescriptionForNumber(number, langStr, scriptStr,
+ regionStr);
+ }
return (areaDescription.length() > 0)
? areaDescription : getCountryNameForNumber(number, languageCode);
}
numberType == PhoneNumberType.MOBILE ||
numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE);
}
-
- /**
- * Returns an area-level text description in the given language for the given phone number.
- *
- * @param number the phone number for which we want to get a text description
- * @param lang two-letter lowercase ISO language codes as defined by ISO 639-1
- * @param script four-letter titlecase (the first letter is uppercase and the rest of the letters
- * are lowercase) ISO script codes as defined in ISO 15924
- * @param region two-letter uppercase ISO country codes as defined by ISO 3166-1
- * @return an area-level text description in the given language for the given phone number, or an
- * empty string if such a description is not available
- */
- private String getAreaDescriptionForNumber(
- PhoneNumber number, String lang, String script, String region) {
- int countryCallingCode = number.getCountryCode();
- // As the NANPA data is split into multiple files covering 3-digit areas, use a phone number
- // prefix of 4 digits for NANPA instead, e.g. 1650.
- int phonePrefix = (countryCallingCode != 1) ?
- countryCallingCode : (1000 + (int) (number.getNationalNumber() / 10000000));
- AreaCodeMap phonePrefixDescriptions =
- getPhonePrefixDescriptions(phonePrefix, lang, script, region);
- String description = (phonePrefixDescriptions != null)
- ? phonePrefixDescriptions.lookup(number)
- : null;
- // When a location is not available in the requested language, fall back to English.
- if ((description == null || description.length() == 0) && mayFallBackToEnglish(lang)) {
- AreaCodeMap defaultMap = getPhonePrefixDescriptions(phonePrefix, "en", "", "");
- if (defaultMap == null) {
- return "";
- }
- description = defaultMap.lookup(number);
- }
- return description != null ? description : "";
- }
-
- private boolean mayFallBackToEnglish(String lang) {
- // Don't fall back to English if the requested language is among the following:
- // - Chinese
- // - Japanese
- // - Korean
- return !lang.equals("zh") && !lang.equals("ja") && !lang.equals("ko");
- }
}
--- /dev/null
+/*
+ * Copyright (C) 2011 The Libphonenumber Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers.geocoding;
+
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A helper class doing file handling and lookup of phone number prefix mappings.
+ *
+ * @author Shaopeng Jia
+ */
+public class PrefixFileReader {
+ private static final Logger LOGGER = Logger.getLogger(PrefixFileReader.class.getName());
+
+ private final String phonePrefixDataDirectory;
+ // The mappingFileProvider knows for which combination of countryCallingCode and language a phone
+ // prefix mapping file is available in the file system, so that a file can be loaded when needed.
+ private MappingFileProvider mappingFileProvider = new MappingFileProvider();
+ // A mapping from countryCallingCode_lang to the corresponding phone prefix map that has been
+ // loaded.
+ private Map<String, AreaCodeMap> availablePhonePrefixMaps = new HashMap<String, AreaCodeMap>();
+
+ public PrefixFileReader(String phonePrefixDataDirectory) {
+ this.phonePrefixDataDirectory = phonePrefixDataDirectory;
+ loadMappingFileProvider();
+ }
+
+ private void loadMappingFileProvider() {
+ InputStream source =
+ PrefixFileReader.class.getResourceAsStream(phonePrefixDataDirectory + "config");
+ ObjectInputStream in = null;
+ try {
+ in = new ObjectInputStream(source);
+ mappingFileProvider.readExternal(in);
+ } catch (IOException e) {
+ LOGGER.log(Level.WARNING, e.toString());
+ } finally {
+ close(in);
+ }
+ }
+
+ private AreaCodeMap getPhonePrefixDescriptions(
+ int prefixMapKey, String language, String script, String region) {
+ String fileName = mappingFileProvider.getFileName(prefixMapKey, language, script, region);
+ if (fileName.length() == 0) {
+ return null;
+ }
+ if (!availablePhonePrefixMaps.containsKey(fileName)) {
+ loadAreaCodeMapFromFile(fileName);
+ }
+ return availablePhonePrefixMaps.get(fileName);
+ }
+
+ private void loadAreaCodeMapFromFile(String fileName) {
+ InputStream source =
+ PrefixFileReader.class.getResourceAsStream(phonePrefixDataDirectory + fileName);
+ ObjectInputStream in = null;
+ try {
+ in = new ObjectInputStream(source);
+ AreaCodeMap map = new AreaCodeMap();
+ map.readExternal(in);
+ availablePhonePrefixMaps.put(fileName, map);
+ } catch (IOException e) {
+ LOGGER.log(Level.WARNING, e.toString());
+ } finally {
+ close(in);
+ }
+ }
+
+ private static void close(InputStream in) {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ LOGGER.log(Level.WARNING, e.toString());
+ }
+ }
+ }
+
+ /**
+ * Returns a text description in the given language for the given phone number.
+ *
+ * @param number the phone number for which we want to get a text description
+ * @param lang two-letter lowercase ISO language codes as defined by ISO 639-1
+ * @param script four-letter titlecase (the first letter is uppercase and the rest of the letters
+ * are lowercase) ISO script codes as defined in ISO 15924
+ * @param region two-letter uppercase ISO country codes as defined by ISO 3166-1
+ * @return a text description in the given language for the given phone number, or an empty
+ * string if a description is not available
+ */
+ public String getDescriptionForNumber(
+ PhoneNumber number, String lang, String script, String region) {
+ int countryCallingCode = number.getCountryCode();
+ // As the NANPA data is split into multiple files covering 3-digit areas, use a phone number
+ // prefix of 4 digits for NANPA instead, e.g. 1650.
+ int phonePrefix = (countryCallingCode != 1) ?
+ countryCallingCode : (1000 + (int) (number.getNationalNumber() / 10000000));
+ AreaCodeMap phonePrefixDescriptions =
+ getPhonePrefixDescriptions(phonePrefix, lang, script, region);
+ String description = (phonePrefixDescriptions != null)
+ ? phonePrefixDescriptions.lookup(number)
+ : null;
+ // When a location is not available in the requested language, fall back to English.
+ if ((description == null || description.length() == 0) && mayFallBackToEnglish(lang)) {
+ AreaCodeMap defaultMap = getPhonePrefixDescriptions(phonePrefix, "en", "", "");
+ if (defaultMap == null) {
+ return "";
+ }
+ description = defaultMap.lookup(number);
+ }
+ return description != null ? description : "";
+ }
+
+ private boolean mayFallBackToEnglish(String lang) {
+ // Don't fall back to English if the requested language is among the following:
+ // - Chinese
+ // - Japanese
+ // - Korean
+ return !lang.equals("zh") && !lang.equals("ja") && !lang.equals("ko");
+ }
+}
new PhoneNumber().setCountryCode(1).setNationalNumber(2423651234L);
private static final PhoneNumber AU_NUMBER =
new PhoneNumber().setCountryCode(61).setNationalNumber(236618300L);
+ private static final PhoneNumber AR_MOBILE_NUMBER =
+ new PhoneNumber().setCountryCode(54).setNationalNumber(92214000000L);
private static final PhoneNumber NUMBER_WITH_INVALID_COUNTRY_CODE =
new PhoneNumber().setCountryCode(999).setNationalNumber(2423651234L);
private static final PhoneNumber INTERNATIONAL_TOLL_FREE =
geocoder.getDescriptionForNumber(KO_NUMBER2, Locale.KOREAN));
}
+ public void testGetDescriptionForArgentinianMobileNumber() {
+ assertEquals("La Plata",
+ geocoder.getDescriptionForNumber(AR_MOBILE_NUMBER, Locale.ENGLISH));
+ }
+
public void testGetDescriptionForFallBack() {
// No fallback, as the location name for the given phone number is available in the requested
// language.
--- /dev/null
+/*
+ * Copyright (C) 2013 The Libphonenumber Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers.geocoding;
+
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for PrefixFileReader.java
+ *
+ * @author Cecilia Roes
+ */
+public class PrefixFileReaderTest extends TestCase {
+ private final PrefixFileReader reader = new PrefixFileReader(TEST_MAPPING_DATA_DIRECTORY);
+ private static final String TEST_MAPPING_DATA_DIRECTORY =
+ "/com/google/i18n/phonenumbers/geocoding/testing_data/";
+
+ private static final PhoneNumber KO_NUMBER =
+ new PhoneNumber().setCountryCode(82).setNationalNumber(22123456L);
+ private static final PhoneNumber US_NUMBER1 =
+ new PhoneNumber().setCountryCode(1).setNationalNumber(6502530000L);
+ private static final PhoneNumber US_NUMBER2 =
+ new PhoneNumber().setCountryCode(1).setNationalNumber(2128120000L);
+ private static final PhoneNumber US_NUMBER3 =
+ new PhoneNumber().setCountryCode(1).setNationalNumber(6174240000L);
+ private static final PhoneNumber SE_NUMBER =
+ new PhoneNumber().setCountryCode(46).setNationalNumber(81234567L);
+
+ public void testGetDescriptionForNumberWithMapping() {
+ assertEquals("Kalifornien",
+ reader.getDescriptionForNumber(US_NUMBER1, "de", "", "CH"));
+ assertEquals("CA",
+ reader.getDescriptionForNumber(US_NUMBER1, "en", "", "AU"));
+ assertEquals("\uC11C\uC6B8",
+ reader.getDescriptionForNumber(KO_NUMBER, "ko", "", ""));
+ assertEquals("Seoul",
+ reader.getDescriptionForNumber(KO_NUMBER, "en", "", ""));
+ }
+
+ public void testGetDescriptionForNumberWithMissingMapping() {
+ assertEquals("", reader.getDescriptionForNumber(US_NUMBER3, "en", "", ""));
+ }
+
+ public void testGetDescriptionUsingFallbackLanguage() {
+ // Mapping file exists but the number isn't present, causing it to fallback.
+ assertEquals("New York, NY",
+ reader.getDescriptionForNumber(US_NUMBER2, "de", "", "CH"));
+ // No mapping file exists, causing it to fallback.
+ assertEquals("New York, NY",
+ reader.getDescriptionForNumber(US_NUMBER2, "sv", "", ""));
+ }
+
+ public void testGetDescriptionForNonFallbackLanguage() {
+ assertEquals("", reader.getDescriptionForNumber(US_NUMBER2, "ko", "", ""));
+ }
+
+ public void testGetDescriptionForNumberWithoutMappingFile() {
+ assertEquals("", reader.getDescriptionForNumber(SE_NUMBER, "sv", "", ""));
+ assertEquals("", reader.getDescriptionForNumber(SE_NUMBER, "en", "", ""));
+ }
+}
+Sep 20, 2013: libphonenumber-5.8.3
+* Code changes:
+ - PhoneNumberOfflineGeocoder: Moved utility functionality to PrefixFileReader.
+ - Bug fix: Argentinian (and other countries with mobile tokens) mobile numbers now geocode
+ correctly.
+
Sep 19, 2013: libphonenumber-5.8.2
* Code changes:
- New method in the PhoneNumberUtil API - getCountryMobileToken.
--- /dev/null
+# Copyright (C) 2013 The Libphonenumber Authors
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+542214|La Plata