1 /* 2 * Copyright (C) 2011 The Libphonenumber Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.i18n.phonenumbers.prefixmapper; 18 19 import com.google.i18n.phonenumbers.MetadataLoader; 20 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; 21 22 import com.google.i18n.phonenumbers.metadata.DefaultMetadataDependenciesProvider; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.ObjectInputStream; 26 import java.util.HashMap; 27 import java.util.Map; 28 import java.util.logging.Level; 29 import java.util.logging.Logger; 30 31 /** 32 * A helper class doing file handling and lookup of phone number prefix mappings. 33 * 34 * @author Shaopeng Jia 35 */ 36 public class PrefixFileReader { 37 private static final Logger logger = Logger.getLogger(PrefixFileReader.class.getName()); 38 39 private final String phonePrefixDataDirectory; 40 // The mappingFileProvider knows for which combination of countryCallingCode and language a phone 41 // prefix mapping file is available in the file system, so that a file can be loaded when needed. 42 private MappingFileProvider mappingFileProvider = new MappingFileProvider(); 43 // A mapping from countryCallingCode_lang to the corresponding phone prefix map that has been 44 // loaded. 45 private Map<String, PhonePrefixMap> availablePhonePrefixMaps = new HashMap<>(); 46 private final MetadataLoader metadataLoader; 47 PrefixFileReader(String phonePrefixDataDirectory)48 public PrefixFileReader(String phonePrefixDataDirectory) { 49 this.phonePrefixDataDirectory = phonePrefixDataDirectory; 50 this.metadataLoader = DefaultMetadataDependenciesProvider.getInstance().getMetadataLoader(); 51 loadMappingFileProvider(); 52 } 53 loadMappingFileProvider()54 private void loadMappingFileProvider() { 55 InputStream source = metadataLoader.loadMetadata(phonePrefixDataDirectory + "config"); 56 ObjectInputStream in = null; 57 try { 58 in = new ObjectInputStream(source); 59 mappingFileProvider.readExternal(in); 60 } catch (IOException e) { 61 logger.log(Level.WARNING, e.toString()); 62 } finally { 63 close(in); 64 } 65 } 66 getPhonePrefixDescriptions( int prefixMapKey, String language, String script, String region)67 private PhonePrefixMap getPhonePrefixDescriptions( 68 int prefixMapKey, String language, String script, String region) { 69 String fileName = mappingFileProvider.getFileName(prefixMapKey, language, script, region); 70 if (fileName.length() == 0) { 71 return null; 72 } 73 if (!availablePhonePrefixMaps.containsKey(fileName)) { 74 loadPhonePrefixMapFromFile(fileName); 75 } 76 return availablePhonePrefixMaps.get(fileName); 77 } 78 loadPhonePrefixMapFromFile(String fileName)79 private void loadPhonePrefixMapFromFile(String fileName) { 80 InputStream source = metadataLoader.loadMetadata(phonePrefixDataDirectory + fileName); 81 ObjectInputStream in = null; 82 try { 83 in = new ObjectInputStream(source); 84 PhonePrefixMap map = new PhonePrefixMap(); 85 map.readExternal(in); 86 availablePhonePrefixMaps.put(fileName, map); 87 } catch (IOException e) { 88 logger.log(Level.WARNING, e.toString()); 89 } finally { 90 close(in); 91 } 92 } 93 close(InputStream in)94 private static void close(InputStream in) { 95 if (in != null) { 96 try { 97 in.close(); 98 } catch (IOException e) { 99 logger.log(Level.WARNING, e.toString()); 100 } 101 } 102 } 103 104 /** 105 * Returns a text description in the given language for the given phone number. 106 * 107 * @param number the phone number for which we want to get a text description 108 * @param language two or three-letter lowercase ISO language codes as defined by ISO 639. Note 109 * that where two different language codes exist (e.g. 'he' and 'iw' for Hebrew) we use the 110 * one that Java/Android canonicalized on ('iw' in this case). 111 * @param script four-letter titlecase (the first letter is uppercase and the rest of the letters 112 * are lowercase) ISO script code as defined in ISO 15924 113 * @param region two-letter uppercase ISO country code as defined by ISO 3166-1 114 * @return a text description in the given language for the given phone number, or an empty 115 * string if a description is not available 116 */ getDescriptionForNumber( PhoneNumber number, String language, String script, String region)117 public String getDescriptionForNumber( 118 PhoneNumber number, String language, String script, String region) { 119 int countryCallingCode = number.getCountryCode(); 120 // As the NANPA data is split into multiple files covering 3-digit areas, use a phone number 121 // prefix of 4 digits for NANPA instead, e.g. 1650. 122 int phonePrefix = (countryCallingCode != 1) 123 ? countryCallingCode : (1000 + (int) (number.getNationalNumber() / 10000000)); 124 PhonePrefixMap phonePrefixDescriptions = 125 getPhonePrefixDescriptions(phonePrefix, language, script, region); 126 String description = (phonePrefixDescriptions != null) 127 ? phonePrefixDescriptions.lookup(number) : null; 128 // When a location is not available in the requested language, fall back to English. 129 if ((description == null || description.length() == 0) && mayFallBackToEnglish(language)) { 130 PhonePrefixMap defaultMap = getPhonePrefixDescriptions(phonePrefix, "en", "", ""); 131 if (defaultMap == null) { 132 return ""; 133 } 134 description = defaultMap.lookup(number); 135 } 136 return description != null ? description : ""; 137 } 138 mayFallBackToEnglish(String lang)139 private boolean mayFallBackToEnglish(String lang) { 140 // Don't fall back to English if the requested language is among the following: 141 // - Chinese 142 // - Japanese 143 // - Korean 144 return !lang.equals("zh") && !lang.equals("ja") && !lang.equals("ko"); 145 } 146 } 147