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/phonemetadata.pb.h"
26#include "phonenumbers/phonenumberutil.h"
27#include "phonenumbers/regexp_adapter.h"
28#include "phonenumbers/regexp_factory.h"
29#include "phonenumbers/short_metadata.h"
30
31namespace i18n {
32namespace phonenumbers {
33
34using std::make_pair;
35using std::map;
36using std::string;
37
38bool 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
47ShortNumberInfo::ShortNumberInfo()
48    : phone_util_(*PhoneNumberUtil::GetInstance()),
49      region_to_short_metadata_map_(new map<string, PhoneMetadata>()) {
50  PhoneMetadataCollection metadata_collection;
51  if (!LoadCompiledInMetadata(&metadata_collection)) {
52    LOG(DFATAL) << "Could not parse compiled-in metadata.";
53    return;
54  }
55  for (RepeatedPtrField<PhoneMetadata>::const_iterator it =
56           metadata_collection.metadata().begin();
57       it != metadata_collection.metadata().end();
58       ++it) {
59    const string& region_code = it->id();
60    region_to_short_metadata_map_->insert(make_pair(region_code, *it));
61  }
62}
63
64// Returns a pointer to the phone metadata for the appropriate region or NULL
65// if the region code is invalid or unknown.
66const PhoneMetadata* ShortNumberInfo::GetMetadataForRegion(
67    const string& region_code) const {
68  map<string, PhoneMetadata>::const_iterator it =
69      region_to_short_metadata_map_->find(region_code);
70  if (it != region_to_short_metadata_map_->end()) {
71    return &it->second;
72  }
73  return NULL;
74}
75
76bool ShortNumberInfo::ConnectsToEmergencyNumber(const string& number,
77    const string& region_code) const {
78  return MatchesEmergencyNumberHelper(number, region_code,
79      true /* allows prefix match */);
80}
81
82bool ShortNumberInfo::IsEmergencyNumber(const string& number,
83    const string& region_code) const {
84  return MatchesEmergencyNumberHelper(number, region_code,
85      false /* doesn't allow prefix match */);
86}
87
88bool ShortNumberInfo::MatchesEmergencyNumberHelper(const string& number,
89    const string& region_code, bool allow_prefix_match) const {
90  string extracted_number;
91  phone_util_.ExtractPossibleNumber(number, &extracted_number);
92  if (phone_util_.StartsWithPlusCharsPattern(extracted_number)) {
93    // Returns false if the number starts with a plus sign. We don't believe
94    // dialing the country code before emergency numbers (e.g. +1911) works,
95    // but later, if that proves to work, we can add additional logic here to
96    // handle it.
97    return false;
98  }
99  const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
100  if (!metadata || !metadata->has_emergency()) {
101    return false;
102  }
103  const scoped_ptr<const AbstractRegExpFactory> regexp_factory(
104      new RegExpFactory());
105  const scoped_ptr<const RegExp> emergency_number_pattern(
106      regexp_factory->CreateRegExp(
107          metadata->emergency().national_number_pattern()));
108  phone_util_.NormalizeDigitsOnly(&extracted_number);
109  const scoped_ptr<RegExpInput> normalized_number_input(
110      regexp_factory->CreateInput(extracted_number));
111
112  // In Brazil and Chile, emergency numbers don't work when additional digits
113  // are appended.
114  return (!allow_prefix_match ||
115      region_code == "BR" || region_code == "CL")
116      ? emergency_number_pattern->FullMatch(extracted_number)
117      : emergency_number_pattern->Consume(normalized_number_input.get());
118}
119
120}  // namespace phonenumbers
121}  // namespace i18n
122