1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 /*
3  * Copyright (C) 2012 The Libphonenumber Authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.i18n.phonenumbers;
19 
20 import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
21 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
22 import com.android.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap;
23 
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.ObjectInputStream;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32 
33 /**
34  * An offline mapper from phone numbers to time zones.
35  * @hide This class is not part of the Android public SDK API
36  */
37 public class PhoneNumberToTimeZonesMapper {
38   private static final String MAPPING_DATA_DIRECTORY =
39       "/com/android/i18n/phonenumbers/timezones/data/";
40   private static final String MAPPING_DATA_FILE_NAME = "map_data";
41   // This is defined by ICU as the unknown time zone.
42   private static final String UNKNOWN_TIMEZONE = "Etc/Unknown";
43   // A list with the ICU unknown time zone as single element.
44   // @VisibleForTesting
45   static final List<String> UNKNOWN_TIME_ZONE_LIST = new ArrayList<String>(1);
46   static {
47     UNKNOWN_TIME_ZONE_LIST.add(UNKNOWN_TIMEZONE);
48   }
49 
50   private static final Logger logger =
51       Logger.getLogger(PhoneNumberToTimeZonesMapper.class.getName());
52 
53   private PrefixTimeZonesMap prefixTimeZonesMap = null;
54 
55   // @VisibleForTesting
PhoneNumberToTimeZonesMapper(String prefixTimeZonesMapDataDirectory)56   PhoneNumberToTimeZonesMapper(String prefixTimeZonesMapDataDirectory) {
57     this.prefixTimeZonesMap = loadPrefixTimeZonesMapFromFile(
58         prefixTimeZonesMapDataDirectory + MAPPING_DATA_FILE_NAME);
59   }
60 
PhoneNumberToTimeZonesMapper(PrefixTimeZonesMap prefixTimeZonesMap)61   private PhoneNumberToTimeZonesMapper(PrefixTimeZonesMap prefixTimeZonesMap) {
62     this.prefixTimeZonesMap = prefixTimeZonesMap;
63   }
64 
loadPrefixTimeZonesMapFromFile(String path)65   private static PrefixTimeZonesMap loadPrefixTimeZonesMapFromFile(String path) {
66     InputStream source = PhoneNumberToTimeZonesMapper.class.getResourceAsStream(path);
67     ObjectInputStream in = null;
68     PrefixTimeZonesMap map = new PrefixTimeZonesMap();
69     try {
70       in = new ObjectInputStream(source);
71       map.readExternal(in);
72     } catch (IOException e) {
73       logger.log(Level.WARNING, e.toString());
74     } finally {
75       close(in);
76     }
77     return map;
78   }
79 
close(InputStream in)80   private static void close(InputStream in) {
81     if (in != null) {
82       try {
83         in.close();
84       } catch (IOException e) {
85         logger.log(Level.WARNING, e.toString());
86       }
87     }
88   }
89 
90   /**
91    * Helper class used for lazy instantiation of a PhoneNumberToTimeZonesMapper. This also loads the
92    * map data in a thread-safe way.
93    */
94   private static class LazyHolder {
95     private static final PhoneNumberToTimeZonesMapper INSTANCE;
96     static {
97       PrefixTimeZonesMap map =
98           loadPrefixTimeZonesMapFromFile(MAPPING_DATA_DIRECTORY + MAPPING_DATA_FILE_NAME);
99       INSTANCE = new PhoneNumberToTimeZonesMapper(map);
100     }
101   }
102 
103   /**
104    * Gets a {@link PhoneNumberToTimeZonesMapper} instance.
105    *
106    * <p> The {@link PhoneNumberToTimeZonesMapper} is implemented as a singleton. Therefore, calling
107    * this method multiple times will only result in one instance being created.
108    *
109    * @return  a {@link PhoneNumberToTimeZonesMapper} instance
110    */
getInstance()111   public static synchronized PhoneNumberToTimeZonesMapper getInstance() {
112     return LazyHolder.INSTANCE;
113   }
114 
115   /**
116    * Returns a list of time zones to which a phone number belongs.
117    *
118    * <p>This method assumes the validity of the number passed in has already been checked, and that
119    * the number is geo-localizable. We consider fixed-line and mobile numbers possible candidates
120    * for geo-localization.
121    *
122    * @param number  a valid phone number for which we want to get the time zones to which it belongs
123    * @return  a list of the corresponding time zones or a single element list with the default
124    *     unknown time zone if no other time zone was found or if the number was invalid
125    */
getTimeZonesForGeographicalNumber(PhoneNumber number)126   public List<String> getTimeZonesForGeographicalNumber(PhoneNumber number) {
127     return getTimeZonesForGeocodableNumber(number);
128   }
129 
130   /**
131    * As per {@link #getTimeZonesForGeographicalNumber(PhoneNumber)} but explicitly checks
132    * the validity of the number passed in.
133    *
134    * @param number  the phone number for which we want to get the time zones to which it belongs
135    * @return  a list of the corresponding time zones or a single element list with the default
136    *     unknown time zone if no other time zone was found or if the number was invalid
137    */
getTimeZonesForNumber(PhoneNumber number)138   public List<String> getTimeZonesForNumber(PhoneNumber number) {
139     PhoneNumberType numberType = PhoneNumberUtil.getInstance().getNumberType(number);
140     if (numberType == PhoneNumberType.UNKNOWN) {
141       return UNKNOWN_TIME_ZONE_LIST;
142     } else if (!PhoneNumberUtil.getInstance().isNumberGeographical(
143         numberType, number.getCountryCode())) {
144       return getCountryLevelTimeZonesforNumber(number);
145     }
146     return getTimeZonesForGeographicalNumber(number);
147   }
148 
149   /**
150    * Returns a String with the ICU unknown time zone.
151    */
getUnknownTimeZone()152   public static String getUnknownTimeZone() {
153     return UNKNOWN_TIMEZONE;
154   }
155 
156   /**
157    * Returns a list of time zones to which a geocodable phone number belongs.
158    *
159    * @param number  the phone number for which we want to get the time zones to which it belongs
160    * @return  the list of corresponding  time zones or a single element list with the default
161    *     unknown time zone if no other time zone was found or if the number was invalid
162    */
getTimeZonesForGeocodableNumber(PhoneNumber number)163   private List<String> getTimeZonesForGeocodableNumber(PhoneNumber number) {
164     List<String> timezones = prefixTimeZonesMap.lookupTimeZonesForNumber(number);
165     return Collections.unmodifiableList(timezones.isEmpty() ? UNKNOWN_TIME_ZONE_LIST
166                                                             : timezones);
167   }
168 
169   /**
170    * Returns the list of time zones corresponding to the country calling code of {@code number}.
171    *
172    * @param number  the phone number to look up
173    * @return  the list of corresponding time zones or a single element list with the default
174    *     unknown time zone if no other time zone was found
175    */
getCountryLevelTimeZonesforNumber(PhoneNumber number)176   private List<String> getCountryLevelTimeZonesforNumber(PhoneNumber number) {
177     List<String> timezones = prefixTimeZonesMap.lookupCountryLevelTimeZonesForNumber(number);
178     return Collections.unmodifiableList(timezones.isEmpty() ? UNKNOWN_TIME_ZONE_LIST
179                                                             : timezones);
180   }
181 }
182