summaryrefslogtreecommitdiff
path: root/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PrefixFileReader.java
blob: 8a4fde70f653764c27bc88b4e91436b766f67193 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
 * 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.prefixmapper;

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, PhonePrefixMap> availablePhonePrefixMaps =
      new HashMap<String, PhonePrefixMap>();

  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 PhonePrefixMap 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)) {
      loadPhonePrefixMapFromFile(fileName);
    }
    return availablePhonePrefixMaps.get(fileName);
  }

  private void loadPhonePrefixMapFromFile(String fileName) {
    InputStream source =
        PrefixFileReader.class.getResourceAsStream(phonePrefixDataDirectory + fileName);
    ObjectInputStream in = null;
    try {
      in = new ObjectInputStream(source);
      PhonePrefixMap map = new PhonePrefixMap();
      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 language  two or three-letter lowercase ISO language codes as defined by ISO 639. Note
   *     that where two different language codes exist (e.g. 'he' and 'iw' for Hebrew) we use the
   *     one that Java/Android canonicalized on ('iw' in this case).
   * @param script  four-letter titlecase (the first letter is uppercase and the rest of the letters
   *     are lowercase) ISO script code as defined in ISO 15924
   * @param region  two-letter uppercase ISO country code 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 language, 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));
    PhonePrefixMap phonePrefixDescriptions =
        getPhonePrefixDescriptions(phonePrefix, language, 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(language)) {
      PhonePrefixMap 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");
  }
}