|
|
/*
|
|
|
* 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 java.io.Externalizable;
|
|
|
import java.io.IOException;
|
|
|
import java.io.ObjectInput;
|
|
|
import java.io.ObjectOutput;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Set;
|
|
|
import java.util.SortedMap;
|
|
|
import java.util.SortedSet;
|
|
|
import java.util.TreeSet;
|
|
|
|
|
|
/**
|
|
|
* A utility which knows the data files that are available for the geocoder to use. The data files
|
|
|
* contain mappings from phone number prefixes to text descriptions, and are organized by country
|
|
|
* calling code and language that the text descriptions are in.
|
|
|
*
|
|
|
* @author Shaopeng Jia
|
|
|
*/
|
|
|
public class MappingFileProvider implements Externalizable {
|
|
|
private int numOfEntries = 0;
|
|
|
private int[] countryCallingCodes;
|
|
|
private List<Set<String>> availableLanguages;
|
|
|
private static final Map<String, String> LOCALE_NORMALIZATION_MAP;
|
|
|
|
|
|
static {
|
|
|
Map<String, String> normalizationMap = new HashMap<String, String>();
|
|
|
normalizationMap.put("zh_TW", "zh_Hant");
|
|
|
normalizationMap.put("zh_HK", "zh_Hant");
|
|
|
normalizationMap.put("zh_MO", "zh_Hant");
|
|
|
|
|
|
LOCALE_NORMALIZATION_MAP = Collections.unmodifiableMap(normalizationMap);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Creates an empty {@link MappingFileProvider}. The default constructor is necessary for
|
|
|
* implementing {@link Externalizable}. The empty provider could later be populated by
|
|
|
* {@link #readFileConfigs(java.util.SortedMap)} or {@link #readExternal(java.io.ObjectInput)}.
|
|
|
*/
|
|
|
public MappingFileProvider() {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Initializes an {@link MappingFileProvider} with {@code availableDataFiles}.
|
|
|
*
|
|
|
* @param availableDataFiles a map from country calling codes to sets of languages in which data
|
|
|
* files are available for the specific country calling code. The map is sorted in ascending
|
|
|
* order of the country calling codes as integers.
|
|
|
*/
|
|
|
public void readFileConfigs(SortedMap<Integer, Set<String>> availableDataFiles) {
|
|
|
numOfEntries = availableDataFiles.size();
|
|
|
countryCallingCodes = new int[numOfEntries];
|
|
|
availableLanguages = new ArrayList<Set<String>>(numOfEntries);
|
|
|
int index = 0;
|
|
|
for (int countryCallingCode : availableDataFiles.keySet()) {
|
|
|
countryCallingCodes[index++] = countryCallingCode;
|
|
|
availableLanguages.add(new HashSet<String>(availableDataFiles.get(countryCallingCode)));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Supports Java Serialization.
|
|
|
*/
|
|
|
public void readExternal(ObjectInput objectInput) throws IOException {
|
|
|
numOfEntries = objectInput.readInt();
|
|
|
if (countryCallingCodes == null || countryCallingCodes.length < numOfEntries) {
|
|
|
countryCallingCodes = new int[numOfEntries];
|
|
|
}
|
|
|
if (availableLanguages == null) {
|
|
|
availableLanguages = new ArrayList<Set<String>>();
|
|
|
}
|
|
|
for (int i = 0; i < numOfEntries; i++) {
|
|
|
countryCallingCodes[i] = objectInput.readInt();
|
|
|
int numOfLangs = objectInput.readInt();
|
|
|
Set<String> setOfLangs = new HashSet<String>();
|
|
|
for (int j = 0; j < numOfLangs; j++) {
|
|
|
setOfLangs.add(objectInput.readUTF());
|
|
|
}
|
|
|
availableLanguages.add(setOfLangs);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Supports Java Serialization.
|
|
|
*/
|
|
|
public void writeExternal(ObjectOutput objectOutput) throws IOException {
|
|
|
objectOutput.writeInt(numOfEntries);
|
|
|
for (int i = 0; i < numOfEntries; i++) {
|
|
|
objectOutput.writeInt(countryCallingCodes[i]);
|
|
|
Set<String> setOfLangs = availableLanguages.get(i);
|
|
|
int numOfLangs = setOfLangs.size();
|
|
|
objectOutput.writeInt(numOfLangs);
|
|
|
for (String lang : setOfLangs) {
|
|
|
objectOutput.writeUTF(lang);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns a string representing the data in this class. The string contains one line for each
|
|
|
* country calling code. The country calling code is followed by a '|' and then a list of
|
|
|
* comma-separated languages sorted in ascending order.
|
|
|
*/
|
|
|
@Override
|
|
|
public String toString() {
|
|
|
StringBuilder output = new StringBuilder();
|
|
|
for (int i = 0; i < numOfEntries; i++) {
|
|
|
output.append(countryCallingCodes[i]);
|
|
|
output.append('|');
|
|
|
SortedSet<String> sortedSetOfLangs = new TreeSet<String>(availableLanguages.get(i));
|
|
|
for (String lang : sortedSetOfLangs) {
|
|
|
output.append(lang);
|
|
|
output.append(',');
|
|
|
}
|
|
|
output.append('\n');
|
|
|
}
|
|
|
return output.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Gets the name of the file that contains the mapping data for the {@code countryCallingCode} in
|
|
|
* the language specified.
|
|
|
*
|
|
|
* @param countryCallingCode the country calling code of phone numbers which the data file
|
|
|
* contains
|
|
|
* @param language 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 the name of the file, or empty string if no such file can be found
|
|
|
*/
|
|
|
String getFileName(int countryCallingCode, String language, String script, String region) {
|
|
|
if (language.length() == 0) {
|
|
|
return "";
|
|
|
}
|
|
|
int index = Arrays.binarySearch(countryCallingCodes, countryCallingCode);
|
|
|
if (index < 0) {
|
|
|
return "";
|
|
|
}
|
|
|
Set<String> setOfLangs = availableLanguages.get(index);
|
|
|
if (setOfLangs.size() > 0) {
|
|
|
String languageCode = findBestMatchingLanguageCode(setOfLangs, language, script, region);
|
|
|
if (languageCode.length() > 0) {
|
|
|
StringBuilder fileName = new StringBuilder();
|
|
|
fileName.append(countryCallingCode).append('_').append(languageCode);
|
|
|
return fileName.toString();
|
|
|
}
|
|
|
}
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
private String findBestMatchingLanguageCode(
|
|
|
Set<String> setOfLangs, String language, String script, String region) {
|
|
|
StringBuilder fullLocale = constructFullLocale(language, script, region);
|
|
|
String fullLocaleStr = fullLocale.toString();
|
|
|
String normalizedLocale = LOCALE_NORMALIZATION_MAP.get(fullLocaleStr);
|
|
|
if (normalizedLocale != null) {
|
|
|
if (setOfLangs.contains(normalizedLocale)) {
|
|
|
return normalizedLocale;
|
|
|
}
|
|
|
}
|
|
|
if (setOfLangs.contains(fullLocaleStr)) {
|
|
|
return fullLocaleStr;
|
|
|
}
|
|
|
|
|
|
if (onlyOneOfScriptOrRegionIsEmpty(script, region)) {
|
|
|
if (setOfLangs.contains(language)) {
|
|
|
return language;
|
|
|
}
|
|
|
} else if (script.length() > 0 && region.length() > 0) {
|
|
|
StringBuilder langWithScript = new StringBuilder(language).append('_').append(script);
|
|
|
String langWithScriptStr = langWithScript.toString();
|
|
|
if (setOfLangs.contains(langWithScriptStr)) {
|
|
|
return langWithScriptStr;
|
|
|
}
|
|
|
|
|
|
StringBuilder langWithRegion = new StringBuilder(language).append('_').append(region);
|
|
|
String langWithRegionStr = langWithRegion.toString();
|
|
|
if (setOfLangs.contains(langWithRegionStr)) {
|
|
|
return langWithRegionStr;
|
|
|
}
|
|
|
|
|
|
if (setOfLangs.contains(language)) {
|
|
|
return language;
|
|
|
}
|
|
|
}
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
private boolean onlyOneOfScriptOrRegionIsEmpty(String script, String region) {
|
|
|
return (script.length() == 0 && region.length() > 0) ||
|
|
|
(region.length() == 0 && script.length() > 0);
|
|
|
}
|
|
|
|
|
|
private StringBuilder constructFullLocale(String language, String script, String region) {
|
|
|
StringBuilder fullLocale = new StringBuilder(language);
|
|
|
appendSubsequentLocalePart(script, fullLocale);
|
|
|
appendSubsequentLocalePart(region, fullLocale);
|
|
|
return fullLocale;
|
|
|
}
|
|
|
|
|
|
private void appendSubsequentLocalePart(String subsequentLocalePart, StringBuilder fullLocale) {
|
|
|
if (subsequentLocalePart.length() > 0) {
|
|
|
fullLocale.append('_').append(subsequentLocalePart);
|
|
|
}
|
|
|
}
|
|
|
}
|