Fully quality std::* names.
[platform/upstream/libphonenumber.git] / cpp / src / phonenumbers / shortnumberinfo.cc
1 // Copyright (C) 2012 The Libphonenumber Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Author: David Yonge-Mallo
16
17 #include "phonenumbers/shortnumberinfo.h"
18
19 #include <string.h>
20 #include <iterator>
21 #include <map>
22
23 #include "phonenumbers/base/memory/scoped_ptr.h"
24 #include "phonenumbers/default_logger.h"
25 #include "phonenumbers/matcher_api.h"
26 #include "phonenumbers/phonemetadata.pb.h"
27 #include "phonenumbers/phonenumberutil.h"
28 #include "phonenumbers/regex_based_matcher.h"
29 #include "phonenumbers/region_code.h"
30 #include "phonenumbers/short_metadata.h"
31
32 namespace i18n {
33 namespace phonenumbers {
34
35 using std::map;
36 using std::string;
37
38 bool LoadCompiledInMetadata(PhoneMetadataCollection* metadata) {
39   if (!metadata->ParseFromArray(short_metadata_get(), short_metadata_size())) {
40     LOG(ERROR) << "Could not parse binary data.";
41     return false;
42   }
43   return true;
44 }
45
46 ShortNumberInfo::ShortNumberInfo()
47     : phone_util_(*PhoneNumberUtil::GetInstance()),
48       matcher_api_(new RegexBasedMatcher()),
49       region_to_short_metadata_map_(new map<string, PhoneMetadata>()),
50       regions_where_emergency_numbers_must_be_exact_(new set<string>()) {
51   PhoneMetadataCollection metadata_collection;
52   if (!LoadCompiledInMetadata(&metadata_collection)) {
53     LOG(DFATAL) << "Could not parse compiled-in metadata.";
54     return;
55   }
56   for (RepeatedPtrField<PhoneMetadata>::const_iterator it =
57            metadata_collection.metadata().begin();
58        it != metadata_collection.metadata().end();
59        ++it) {
60     const string& region_code = it->id();
61     region_to_short_metadata_map_->insert(std::make_pair(region_code, *it));
62   }
63   regions_where_emergency_numbers_must_be_exact_->insert("BR");
64   regions_where_emergency_numbers_must_be_exact_->insert("CL");
65   regions_where_emergency_numbers_must_be_exact_->insert("NI");
66 }
67
68 ShortNumberInfo::~ShortNumberInfo() {}
69
70 // Returns a pointer to the phone metadata for the appropriate region or NULL
71 // if the region code is invalid or unknown.
72 const PhoneMetadata* ShortNumberInfo::GetMetadataForRegion(
73     const string& region_code) const {
74   map<string, PhoneMetadata>::const_iterator it =
75       region_to_short_metadata_map_->find(region_code);
76   if (it != region_to_short_metadata_map_->end()) {
77     return &it->second;
78   }
79   return NULL;
80 }
81
82 namespace {
83 // Same as the matchesPossibleNumberAndNationalNumber method in
84 // java/libphonenumber/src/com/google/i18n/phonenumbers/ShortNumberInfo.java
85 // TODO: Once we have benchmarked ShortNumberInfo, consider if it is
86 // worth keeping this performance optimization, and if so move this into the
87 // matcher implementation.
88 bool MatchesPossibleNumberAndNationalNumber(
89     const MatcherApi& matcher_api, const string& number,
90     const PhoneNumberDesc& number_desc) {
91   return matcher_api.MatchesPossibleNumber(number, number_desc) &&
92          matcher_api.MatchesNationalNumber(number, number_desc, false);
93 }
94 }  // namespace
95
96 bool ShortNumberInfo::IsPossibleShortNumberForRegion(
97     const string& short_number, const string& region_dialing_from) const {
98   const PhoneMetadata* phone_metadata =
99       GetMetadataForRegion(region_dialing_from);
100   if (!phone_metadata) {
101     return false;
102   }
103   const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
104   return matcher_api_->MatchesPossibleNumber(short_number, general_desc);
105 }
106
107 bool ShortNumberInfo::IsPossibleShortNumberForRegion(const PhoneNumber& number,
108     const string& region_dialing_from) const {
109   const PhoneMetadata* phone_metadata =
110       GetMetadataForRegion(region_dialing_from);
111   if (!phone_metadata) {
112     return false;
113   }
114   string short_number;
115   phone_util_.GetNationalSignificantNumber(number, &short_number);
116   const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
117   return matcher_api_->MatchesPossibleNumber(short_number, general_desc);
118 }
119
120 bool ShortNumberInfo::IsPossibleShortNumber(const PhoneNumber& number) const {
121   list<string> region_codes;
122   phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
123                                                   &region_codes);
124   string short_number;
125   phone_util_.GetNationalSignificantNumber(number, &short_number);
126   for (list<string>::const_iterator it = region_codes.begin();
127        it != region_codes.end(); ++it) {
128     const PhoneMetadata* phone_metadata = GetMetadataForRegion(*it);
129     if (matcher_api_->MatchesPossibleNumber(short_number,
130                                             phone_metadata->general_desc())) {
131       return true;
132     }
133   }
134   return false;
135 }
136
137 bool ShortNumberInfo::IsValidShortNumberForRegion(
138     const string& short_number, const string& region_dialing_from) const {
139   const PhoneMetadata* phone_metadata =
140       GetMetadataForRegion(region_dialing_from);
141   if (!phone_metadata) {
142     return false;
143   }
144   const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
145   if (!MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
146                                               general_desc)) {
147     return false;
148   }
149   const PhoneNumberDesc& short_number_desc = phone_metadata->short_code();
150   return MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
151                                                 short_number_desc);
152 }
153
154 bool ShortNumberInfo::IsValidShortNumberForRegion(
155     const PhoneNumber& number, const string& region_dialing_from) const {
156   const PhoneMetadata* phone_metadata =
157       GetMetadataForRegion(region_dialing_from);
158   if (!phone_metadata) {
159     return false;
160   }
161   string short_number;
162   phone_util_.GetNationalSignificantNumber(number, &short_number);
163   const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
164   if (!MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
165                                               general_desc)) {
166     return false;
167   }
168   const PhoneNumberDesc& short_number_desc = phone_metadata->short_code();
169   return MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
170                                                 short_number_desc);
171 }
172
173 bool ShortNumberInfo::IsValidShortNumber(const PhoneNumber& number) const {
174   list<string> region_codes;
175   phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
176                                                   &region_codes);
177   string region_code;
178   GetRegionCodeForShortNumberFromRegionList(number, region_codes, &region_code);
179   if (region_codes.size() > 1 && region_code != RegionCode::GetUnknown()) {
180     return true;
181   }
182   return IsValidShortNumberForRegion(number, region_code);
183 }
184
185 ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCostForRegion(
186     const string& short_number, const string& region_dialing_from) const {
187   const PhoneMetadata* phone_metadata =
188       GetMetadataForRegion(region_dialing_from);
189   if (!phone_metadata) {
190     return ShortNumberInfo::UNKNOWN_COST;
191   }
192
193   // The cost categories are tested in order of decreasing expense, since if
194   // for some reason the patterns overlap the most expensive matching cost
195   // category should be returned.
196   if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
197                                              phone_metadata->premium_rate())) {
198     return ShortNumberInfo::PREMIUM_RATE;
199   }
200   if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
201                                              phone_metadata->standard_rate())) {
202     return ShortNumberInfo::STANDARD_RATE;
203   }
204   if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
205                                              phone_metadata->toll_free())) {
206     return ShortNumberInfo::TOLL_FREE;
207   }
208   if (IsEmergencyNumber(short_number, region_dialing_from)) {
209     // Emergency numbers are implicitly toll-free.
210     return ShortNumberInfo::TOLL_FREE;
211   }
212   return ShortNumberInfo::UNKNOWN_COST;
213 }
214
215 ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCostForRegion(
216     const PhoneNumber& number, const string& region_dialing_from) const {
217   const PhoneMetadata* phone_metadata =
218       GetMetadataForRegion(region_dialing_from);
219   if (!phone_metadata) {
220     return ShortNumberInfo::UNKNOWN_COST;
221   }
222   string short_number;
223   phone_util_.GetNationalSignificantNumber(number, &short_number);
224
225   // The cost categories are tested in order of decreasing expense, since if
226   // for some reason the patterns overlap the most expensive matching cost
227   // category should be returned.
228   if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
229                                              phone_metadata->premium_rate())) {
230     return ShortNumberInfo::PREMIUM_RATE;
231   }
232   if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
233                                              phone_metadata->standard_rate())) {
234     return ShortNumberInfo::STANDARD_RATE;
235   }
236   if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
237                                              phone_metadata->toll_free())) {
238     return ShortNumberInfo::TOLL_FREE;
239   }
240   if (IsEmergencyNumber(short_number, region_dialing_from)) {
241     // Emergency numbers are implicitly toll-free.
242     return ShortNumberInfo::TOLL_FREE;
243   }
244   return ShortNumberInfo::UNKNOWN_COST;
245 }
246
247 ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCost(
248     const PhoneNumber& number) const {
249   list<string> region_codes;
250   phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
251                                                   &region_codes);
252   if (region_codes.size() == 0) {
253     return ShortNumberInfo::UNKNOWN_COST;
254   }
255   if (region_codes.size() == 1) {
256     return GetExpectedCostForRegion(number, region_codes.front());
257   }
258   ShortNumberInfo::ShortNumberCost cost = ShortNumberInfo::TOLL_FREE;
259   for (list<string>::const_iterator it = region_codes.begin();
260        it != region_codes.end(); ++it) {
261     ShortNumberInfo::ShortNumberCost cost_for_region =
262         GetExpectedCostForRegion(number, *it);
263     switch (cost_for_region) {
264      case ShortNumberInfo::PREMIUM_RATE:
265        return ShortNumberInfo::PREMIUM_RATE;
266      case ShortNumberInfo::UNKNOWN_COST:
267        return ShortNumberInfo::UNKNOWN_COST;
268      case ShortNumberInfo::STANDARD_RATE:
269        if (cost != ShortNumberInfo::UNKNOWN_COST) {
270          cost = ShortNumberInfo::STANDARD_RATE;
271        }
272        break;
273      case ShortNumberInfo::TOLL_FREE:
274        // Do nothing.
275        break;
276      default:
277        LOG(ERROR) << "Unrecognised cost for region: "
278                   << static_cast<int>(cost_for_region);
279        break;
280     }
281   }
282   return cost;
283 }
284
285 void ShortNumberInfo::GetRegionCodeForShortNumberFromRegionList(
286     const PhoneNumber& number, const list<string>& region_codes,
287     string* region_code) const {
288   if (region_codes.size() == 0) {
289     region_code->assign(RegionCode::GetUnknown());
290     return;
291   } else if (region_codes.size() == 1) {
292     region_code->assign(region_codes.front());
293     return;
294   }
295   string national_number;
296   phone_util_.GetNationalSignificantNumber(number, &national_number);
297   for (list<string>::const_iterator it = region_codes.begin();
298        it != region_codes.end(); ++it) {
299     const PhoneMetadata* phone_metadata = GetMetadataForRegion(*it);
300     if (phone_metadata != NULL &&
301         MatchesPossibleNumberAndNationalNumber(*matcher_api_, national_number,
302                                                phone_metadata->short_code())) {
303       // The number is valid for this region.
304       region_code->assign(*it);
305       return;
306     }
307   }
308   region_code->assign(RegionCode::GetUnknown());
309 }
310
311 string ShortNumberInfo::GetExampleShortNumber(const string& region_code) const {
312   const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
313   if (!phone_metadata) {
314     return "";
315   }
316   const PhoneNumberDesc& desc = phone_metadata->short_code();
317   if (desc.has_example_number()) {
318     return desc.example_number();
319   }
320   return "";
321 }
322
323 string ShortNumberInfo::GetExampleShortNumberForCost(const string& region_code,
324     ShortNumberInfo::ShortNumberCost cost) const {
325   const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
326   if (!phone_metadata) {
327     return "";
328   }
329   const PhoneNumberDesc* desc = NULL;
330   switch (cost) {
331     case TOLL_FREE:
332       desc = &(phone_metadata->toll_free());
333       break;
334     case STANDARD_RATE:
335       desc = &(phone_metadata->standard_rate());
336       break;
337     case PREMIUM_RATE:
338       desc = &(phone_metadata->premium_rate());
339       break;
340     default:
341       // UNKNOWN_COST numbers are computed by the process of elimination from
342       // the other cost categories.
343       break;
344   }
345   if (desc != NULL && desc->has_example_number()) {
346     return desc->example_number();
347   }
348   return "";
349 }
350
351 bool ShortNumberInfo::ConnectsToEmergencyNumber(const string& number,
352     const string& region_code) const {
353   return MatchesEmergencyNumberHelper(number, region_code,
354       true /* allows prefix match */);
355 }
356
357 bool ShortNumberInfo::IsEmergencyNumber(const string& number,
358     const string& region_code) const {
359   return MatchesEmergencyNumberHelper(number, region_code,
360       false /* doesn't allow prefix match */);
361 }
362
363 bool ShortNumberInfo::MatchesEmergencyNumberHelper(const string& number,
364     const string& region_code, bool allow_prefix_match) const {
365   string extracted_number;
366   phone_util_.ExtractPossibleNumber(number, &extracted_number);
367   if (phone_util_.StartsWithPlusCharsPattern(extracted_number)) {
368     // Returns false if the number starts with a plus sign. We don't believe
369     // dialing the country code before emergency numbers (e.g. +1911) works,
370     // but later, if that proves to work, we can add additional logic here to
371     // handle it.
372     return false;
373   }
374   const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
375   if (!metadata || !metadata->has_emergency()) {
376     return false;
377   }
378   phone_util_.NormalizeDigitsOnly(&extracted_number);
379   bool allow_prefix_match_for_region =
380       allow_prefix_match &&
381       regions_where_emergency_numbers_must_be_exact_->find(region_code) ==
382           regions_where_emergency_numbers_must_be_exact_->end();
383   return matcher_api_->MatchesNationalNumber(
384       extracted_number, metadata->emergency(), allow_prefix_match_for_region);
385 }
386
387 bool ShortNumberInfo::IsCarrierSpecific(const PhoneNumber& number) const {
388   list<string> region_codes;
389   phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
390                                                   &region_codes);
391   string region_code;
392   GetRegionCodeForShortNumberFromRegionList(number, region_codes, &region_code);
393   string national_number;
394   phone_util_.GetNationalSignificantNumber(number, &national_number);
395   const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
396   return phone_metadata &&
397          MatchesPossibleNumberAndNationalNumber(*matcher_api_, national_number,
398              phone_metadata->carrier_specific());
399 }
400
401 }  // namespace phonenumbers
402 }  // namespace i18n