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