1/*
2 * Copyright (C) 2012 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
17package com.android.i18n.phonenumbers;
18
19import com.android.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
20import com.android.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
21
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.ObjectInputStream;
25import java.util.Collections;
26import java.util.HashMap;
27import java.util.Iterator;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31import java.util.logging.Level;
32import java.util.logging.Logger;
33
34/**
35 * Class encapsulating loading of PhoneNumber Metadata information. Currently this is used only for
36 * additional data files such as PhoneNumberAlternateFormats, but in the future it is envisaged it
37 * would handle the main metadata file (PhoneNumberMetadata.xml) as well.
38 */
39class MetadataManager {
40  private static final String ALTERNATE_FORMATS_FILE_PREFIX =
41      "/com/android/i18n/phonenumbers/data/PhoneNumberAlternateFormatsProto";
42  private static final String SHORT_NUMBER_METADATA_FILE_PREFIX =
43      "/com/android/i18n/phonenumbers/data/ShortNumberMetadataProto";
44
45  private static final Logger LOGGER = Logger.getLogger(MetadataManager.class.getName());
46
47  private static final Map<Integer, PhoneMetadata> callingCodeToAlternateFormatsMap =
48      Collections.synchronizedMap(new HashMap<Integer, PhoneMetadata>());
49  private static final Map<String, PhoneMetadata> regionCodeToShortNumberMetadataMap =
50      Collections.synchronizedMap(new HashMap<String, PhoneMetadata>());
51
52  // A set of which country calling codes there are alternate format data for. If the set has an
53  // entry for a code, then there should be data for that code linked into the resources.
54  private static final Set<Integer> countryCodeSet =
55      AlternateFormatsCountryCodeSet.getCountryCodeSet();
56
57  // A set of which region codes there are short number data for. If the set has an entry for a
58  // code, then there should be data for that code linked into the resources.
59  private static final Set<String> regionCodeSet = ShortNumbersRegionCodeSet.getRegionCodeSet();
60
61  private MetadataManager() {
62  }
63
64  private static void close(InputStream in) {
65    if (in != null) {
66      try {
67        in.close();
68      } catch (IOException e) {
69        LOGGER.log(Level.WARNING, e.toString());
70      }
71    }
72  }
73
74  private static void loadAlternateFormatsMetadataFromFile(int countryCallingCode) {
75    InputStream source = PhoneNumberMatcher.class.getResourceAsStream(
76        ALTERNATE_FORMATS_FILE_PREFIX + "_" + countryCallingCode);
77    ObjectInputStream in = null;
78    try {
79      in = new ObjectInputStream(source);
80      PhoneMetadataCollection alternateFormats = new PhoneMetadataCollection();
81      alternateFormats.readExternal(in);
82      for (PhoneMetadata metadata : alternateFormats.getMetadataList()) {
83        callingCodeToAlternateFormatsMap.put(metadata.getCountryCode(), metadata);
84      }
85    } catch (IOException e) {
86      LOGGER.log(Level.WARNING, e.toString());
87    } finally {
88      close(in);
89    }
90  }
91
92  static PhoneMetadata getAlternateFormatsForCountry(int countryCallingCode) {
93    if (!countryCodeSet.contains(countryCallingCode)) {
94      return null;
95    }
96    synchronized (callingCodeToAlternateFormatsMap) {
97      if (!callingCodeToAlternateFormatsMap.containsKey(countryCallingCode)) {
98        loadAlternateFormatsMetadataFromFile(countryCallingCode);
99      }
100    }
101    return callingCodeToAlternateFormatsMap.get(countryCallingCode);
102  }
103
104  private static void loadShortNumberMetadataFromFile(String regionCode) {
105    InputStream source = PhoneNumberMatcher.class.getResourceAsStream(
106        SHORT_NUMBER_METADATA_FILE_PREFIX + "_" + regionCode);
107    ObjectInputStream in = null;
108    try {
109      in = new ObjectInputStream(source);
110      PhoneMetadataCollection shortNumberMetadata = new PhoneMetadataCollection();
111      shortNumberMetadata.readExternal(in);
112      for (PhoneMetadata metadata : shortNumberMetadata.getMetadataList()) {
113        regionCodeToShortNumberMetadataMap.put(regionCode, metadata);
114      }
115    } catch (IOException e) {
116      LOGGER.log(Level.WARNING, e.toString());
117    } finally {
118      close(in);
119    }
120  }
121
122  // @VisibleForTesting
123  static Set<String> getShortNumberMetadataSupportedRegions() {
124    return regionCodeSet;
125  }
126
127  static PhoneMetadata getShortNumberMetadataForRegion(String regionCode) {
128    if (!regionCodeSet.contains(regionCode)) {
129      return null;
130    }
131    synchronized (regionCodeToShortNumberMetadataMap) {
132      if (!regionCodeToShortNumberMetadataMap.containsKey(regionCode)) {
133        loadShortNumberMetadataFromFile(regionCode);
134      }
135    }
136    return regionCodeToShortNumberMetadataMap.get(regionCode);
137  }
138}
139