Browse Source

JAVA: Update to geocoder

pull/567/head
Cecilia Roes 12 years ago
committed by Mihaela Rosca
parent
commit
33e35185ec
8 changed files with 269 additions and 116 deletions
  1. +23
    -116
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder.java
  2. +143
    -0
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/PrefixFileReader.java
  3. +7
    -0
      java/geocoder/test/com/google/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoderTest.java
  4. +75
    -0
      java/geocoder/test/com/google/i18n/phonenumbers/geocoding/PrefixFileReaderTest.java
  5. BIN
      java/geocoder/test/com/google/i18n/phonenumbers/geocoding/testing_data/54_en
  6. BIN
      java/geocoder/test/com/google/i18n/phonenumbers/geocoding/testing_data/config
  7. +6
    -0
      java/release_notes.txt
  8. +15
    -0
      resources/test/geocoding/en/54.txt

+ 23
- 116
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder.java View File

@ -19,15 +19,9 @@ package com.google.i18n.phonenumbers.geocoding;
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.
@ -38,75 +32,13 @@ public class PhoneNumberOfflineGeocoder {
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);
}
/**
@ -162,8 +94,25 @@ public class PhoneNumberOfflineGeocoder {
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);
}
@ -256,46 +205,4 @@ public class PhoneNumberOfflineGeocoder {
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");
}
}

+ 143
- 0
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/PrefixFileReader.java View File

@ -0,0 +1,143 @@
/*
* 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");
}
}

+ 7
- 0
java/geocoder/test/com/google/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoderTest.java View File

@ -55,6 +55,8 @@ public class PhoneNumberOfflineGeocoderTest extends TestCase {
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 =
@ -104,6 +106,11 @@ public class PhoneNumberOfflineGeocoderTest extends TestCase {
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.


+ 75
- 0
java/geocoder/test/com/google/i18n/phonenumbers/geocoding/PrefixFileReaderTest.java View File

@ -0,0 +1,75 @@
/*
* 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", "", ""));
}
}

BIN
java/geocoder/test/com/google/i18n/phonenumbers/geocoding/testing_data/54_en View File


BIN
java/geocoder/test/com/google/i18n/phonenumbers/geocoding/testing_data/config View File


+ 6
- 0
java/release_notes.txt View File

@ -1,3 +1,9 @@
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.


+ 15
- 0
resources/test/geocoding/en/54.txt View File

@ -0,0 +1,15 @@
# 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

Loading…
Cancel
Save