Browse Source

CPP: Implement AreaCodeMap and DefaultMapStorage.

pull/567/head
Philippe Liard 14 years ago
committed by Mihaela Rosca
parent
commit
31b159870d
9 changed files with 537 additions and 0 deletions
  1. +3
    -0
      cpp/CMakeLists.txt
  2. +100
    -0
      cpp/src/phonenumbers/geocoding/area_code_map.cc
  3. +76
    -0
      cpp/src/phonenumbers/geocoding/area_code_map.h
  4. +62
    -0
      cpp/src/phonenumbers/geocoding/area_code_map_storage_strategy.h
  5. +71
    -0
      cpp/src/phonenumbers/geocoding/default_map_storage.cc
  6. +67
    -0
      cpp/src/phonenumbers/geocoding/default_map_storage.h
  7. +8
    -0
      cpp/src/phonenumbers/stringutil.cc
  8. +4
    -0
      cpp/src/phonenumbers/stringutil.h
  9. +146
    -0
      cpp/test/phonenumbers/geocoding/area_code_map_test.cc

+ 3
- 0
cpp/CMakeLists.txt View File

@ -159,6 +159,8 @@ set (
"src/base/string_piece.cc"
"src/phonenumbers/asyoutypeformatter.cc"
"src/phonenumbers/default_logger.cc"
"src/phonenumbers/geocoding/area_code_map.cc"
"src/phonenumbers/geocoding/default_map_storage.cc"
"src/phonenumbers/logger.cc"
"src/phonenumbers/metadata.h" # Generated by build tools.
"src/phonenumbers/phonemetadata.pb.cc" # Generated by Protocol Buffers.
@ -310,6 +312,7 @@ add_dependencies (phonenumber_testing generate-sources ${TEST_METADATA_TARGET})
set (TEST_SOURCES
"test/phonenumbers/asyoutypeformatter_test.cc"
"test/phonenumbers/geocoding/area_code_map_test.cc"
"test/phonenumbers/logger_test.cc"
"test/phonenumbers/phonenumberutil_test.cc"
"test/phonenumbers/regexp_adapter_test.cc"


+ 100
- 0
cpp/src/phonenumbers/geocoding/area_code_map.cc View File

@ -0,0 +1,100 @@
// Copyright (C) 2012 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.
//
// Author: Patrick Mezard
#include "phonenumbers/geocoding/area_code_map.h"
#include <cstddef>
#include <iterator>
#include <set>
#include "phonenumbers/geocoding/area_code_map_storage_strategy.h"
#include "phonenumbers/geocoding/default_map_storage.h"
#include "phonenumbers/phonenumber.pb.h"
#include "phonenumbers/phonenumberutil.h"
#include "phonenumbers/stringutil.h"
namespace i18n {
namespace phonenumbers {
AreaCodeMap::AreaCodeMap()
: phone_util_(*PhoneNumberUtil::GetInstance()) {
}
AreaCodeMap::~AreaCodeMap() {
}
AreaCodeMapStorageStrategy* AreaCodeMap::CreateDefaultMapStorage() const {
return new DefaultMapStorage();
}
void AreaCodeMap::ReadAreaCodeMap(const map<int, string>& area_codes) {
AreaCodeMapStorageStrategy* storage = CreateDefaultMapStorage();
storage->ReadFromMap(area_codes);
storage_.reset(storage);
}
const string* AreaCodeMap::Lookup(const PhoneNumber& number) const {
const int entries = storage_->GetNumOfEntries();
if (!entries) {
return NULL;
}
string national_number;
phone_util_.GetNationalSignificantNumber(number, &national_number);
int64 phone_prefix;
safe_strto64(SimpleItoa(number.country_code()) + national_number,
&phone_prefix);
const set<int>& lengths = storage_->GetPossibleLengths();
int current_index = entries - 1;
for (set<int>::const_reverse_iterator lengths_it = lengths.rbegin();
lengths_it != lengths.rend(); ++lengths_it) {
const int possible_length = *lengths_it;
string phone_prefix_str = SimpleItoa(phone_prefix);
if (static_cast<int>(phone_prefix_str.length()) > possible_length) {
safe_strto64(phone_prefix_str.substr(0, possible_length), &phone_prefix);
}
current_index = BinarySearch(0, current_index, phone_prefix);
if (current_index < 0) {
return NULL;
}
const int current_prefix = storage_->GetPrefix(current_index);
if (phone_prefix == current_prefix) {
return &storage_->GetDescription(current_index);
}
}
return NULL;
}
int AreaCodeMap::BinarySearch(int start, int end, int64 value) const {
int current = 0;
while (start <= end) {
current = (start + end) / 2;
int current_value = storage_->GetPrefix(current);
if (current_value == value) {
return current;
} else if (current_value > value) {
--current;
end = current;
} else {
start = current + 1;
}
}
return current;
}
} // namespace phonenumbers
} // namespace i18n

+ 76
- 0
cpp/src/phonenumbers/geocoding/area_code_map.h View File

@ -0,0 +1,76 @@
// Copyright (C) 2012 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.
//
// Author: Patrick Mezard
#ifndef I18N_PHONENUMBERS_AREA_CODE_MAP_H_
#define I18N_PHONENUMBERS_AREA_CODE_MAP_H_
#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
namespace i18n {
namespace phonenumbers {
using std::map;
using std::string;
class AreaCodeMapStorageStrategy;
class PhoneNumber;
class PhoneNumberUtil;
// A utility that maps phone number prefixes to a string describing the
// geographical area the prefix covers.
class AreaCodeMap {
public:
AreaCodeMap();
~AreaCodeMap();
// Returns the description of the geographical area the number corresponds
// to. This method distinguishes the case of an invalid prefix and a prefix
// for which the name is not available in the current language. If the
// description is not available in the current language an empty string is
// returned. If no description was found for the provided number, null is
// returned.
const string* Lookup(const PhoneNumber& number) const;
// Creates an AreaCodeMap initialized with area_codes. Note that the
// underlying implementation of this method is expensive thus should
// not be called by time-critical applications.
//
// area_codes maps phone number prefixes to geographical area description.
void ReadAreaCodeMap(const map<int, string>& area_codes);
private:
AreaCodeMapStorageStrategy* CreateDefaultMapStorage() const;
// Does a binary search for value in the provided array from start to end
// (inclusive). Returns the position if {@code value} is found; otherwise,
// returns the position which has the largest value that is less than value.
// This means if value is the smallest, -1 will be returned.
int BinarySearch(int start, int end, int64 value) const;
const PhoneNumberUtil& phone_util_;
scoped_ptr<const AreaCodeMapStorageStrategy> storage_;
DISALLOW_COPY_AND_ASSIGN(AreaCodeMap);
};
} // namespace phonenumbers
} // namespace i18n
#endif /* I18N_PHONENUMBERS_AREA_CODE_MAP_H_ */

+ 62
- 0
cpp/src/phonenumbers/geocoding/area_code_map_storage_strategy.h View File

@ -0,0 +1,62 @@
// Copyright (C) 2012 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.
//
// Author: Patrick Mezard
//
// Interface for phone numbers area prefixes storage classes.
#ifndef I18N_PHONENUMBERS_AREA_CODE_MAP_STRATEGY_H_
#define I18N_PHONENUMBERS_AREA_CODE_MAP_STRATEGY_H_
#include <map>
#include <set>
#include <string>
namespace i18n {
namespace phonenumbers {
using std::map;
using std::set;
using std::string;
// Abstracts the way area code data is stored into memory. It is used by
// AreaCodeMap to support the most space-efficient storage strategy according
// to the provided data.
class AreaCodeMapStorageStrategy {
public:
virtual ~AreaCodeMapStorageStrategy() {}
// Returns the phone number prefix located at the provided index.
virtual int GetPrefix(int index) const = 0;
// Gets the description corresponding to the phone number prefix located
// at the provided index. If the description is not available in the current
// language an empty string is returned.
virtual const string& GetDescription(int index) const = 0;
// Sets the internal state of the underlying storage implementation from the
// provided area_codes that maps phone number prefixes to description strings.
virtual void ReadFromMap(const map<int, string>& area_codes) = 0;
// Returns the number of entries contained in the area code map.
virtual int GetNumOfEntries() const = 0;
// Returns the set containing the possible lengths of prefixes.
virtual const set<int>& GetPossibleLengths() const = 0;
};
} // namespace phonenumbers
} // namespace i18n
#endif // I18N_PHONENUMBERS_AREA_CODE_MAP_STRATEGY_H_

+ 71
- 0
cpp/src/phonenumbers/geocoding/default_map_storage.cc View File

@ -0,0 +1,71 @@
// Copyright (C) 2012 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.
//
// Author: Patrick Mezard
#include "phonenumbers/geocoding/default_map_storage.h"
#include <math.h>
#include <utility>
#include "base/logging.h"
namespace i18n {
namespace phonenumbers {
using std::map;
using std::set;
using std::string;
DefaultMapStorage::DefaultMapStorage() {
}
DefaultMapStorage::~DefaultMapStorage() {
}
int DefaultMapStorage::GetPrefix(int index) const {
DCHECK_GE(index, 0);
DCHECK_LT(index, static_cast<int>(prefixes_.size()));
return prefixes_[index];
}
const string& DefaultMapStorage::GetDescription(int index) const {
DCHECK_GE(index, 0);
DCHECK_LT(index, static_cast<int>(descriptions_.size()));
return descriptions_[index];
}
void DefaultMapStorage::ReadFromMap(const map<int, string>& area_codes) {
prefixes_.resize(area_codes.size());
descriptions_.resize(area_codes.size());
possible_lengths_.clear();
int index = 0;
for (map<int, string>::const_iterator it = area_codes.begin();
it != area_codes.end(); ++it, ++index) {
prefixes_[index] = it->first;
descriptions_[index] = it->second;
possible_lengths_.insert(static_cast<int>(log10(it->first)) + 1);
}
}
int DefaultMapStorage::GetNumOfEntries() const {
return prefixes_.size();
}
const set<int>& DefaultMapStorage::GetPossibleLengths() const {
return possible_lengths_;
}
} // namespace phonenumbers
} // namespace i18n

+ 67
- 0
cpp/src/phonenumbers/geocoding/default_map_storage.h View File

@ -0,0 +1,67 @@
// Copyright (C) 2012 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.
//
// Author: Patrick Mezard
//
// Default class for storing area codes.
#ifndef I18N_PHONENUMBERS_DEFAULT_MAP_STORAGE_H_
#define I18N_PHONENUMBERS_DEFAULT_MAP_STORAGE_H_
#include <map>
#include <set>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "phonenumbers/geocoding/area_code_map_storage_strategy.h"
namespace i18n {
namespace phonenumbers {
using std::map;
using std::set;
using std::string;
using std::vector;
// Default area code map storage strategy that is used for data not
// containing description duplications. It is mainly intended to avoid
// the overhead of the string table management when it is actually
// unnecessary (i.e no string duplication).
class DefaultMapStorage : public AreaCodeMapStorageStrategy {
public:
DefaultMapStorage();
virtual ~DefaultMapStorage();
virtual int GetPrefix(int index) const;
virtual const string& GetDescription(int index) const;
virtual void ReadFromMap(const map<int, string>& area_codes);
virtual int GetNumOfEntries() const;
virtual const set<int>& GetPossibleLengths() const;
private:
// Sorted sequence of phone number prefixes.
vector<int> prefixes_;
// Sequence of prefix descriptions, in the same order than prefixes_.
vector<string> descriptions_;
// Sequence of unique possible lengths in ascending order.
set<int> possible_lengths_;
DISALLOW_COPY_AND_ASSIGN(DefaultMapStorage);
};
} // namespace phonenumbers
} // namespace i18n
#endif /* I18N_PHONENUMBERS_DEFAULT_MAP_STORAGE_H_ */

+ 8
- 0
cpp/src/phonenumbers/stringutil.cc View File

@ -56,6 +56,10 @@ string SimpleItoa(uint64 n) {
return GenericSimpleItoa(n);
}
string SimpleItoa(int64 n) {
return GenericSimpleItoa(n);
}
bool HasPrefixString(const string& s, const string& prefix) {
return s.size() >= prefix.size() &&
equal(s.begin(), s.begin() + prefix.size(), prefix.begin());
@ -133,6 +137,10 @@ void safe_strtou64(const string& s, uint64 *n) {
GenericAtoi(s, n);
}
void safe_strto64(const string& s, int64* n) {
GenericAtoi(s, n);
}
void strrmm(string* s, const string& chars) {
for (string::iterator it = s->begin(); it != s->end(); ) {
const char current_char = *it;


+ 4
- 0
cpp/src/phonenumbers/stringutil.h View File

@ -34,6 +34,7 @@ string operator+(const string& s, int n); // NOLINT(runtime/string)
// Converts integer to string.
string SimpleItoa(uint64 n);
string SimpleItoa(int64 n);
string SimpleItoa(int n);
// Returns whether the provided string starts with the supplied prefix.
@ -65,6 +66,9 @@ void safe_strto32(const string& s, int32 *n);
// Converts string to uint64.
void safe_strtou64(const string& s, uint64 *n);
// Converts string to int64.
void safe_strto64(const string& s, int64* n);
// Remove all occurrences of a given set of characters from a string.
void strrmm(string* s, const string& chars);


+ 146
- 0
cpp/test/phonenumbers/geocoding/area_code_map_test.cc View File

@ -0,0 +1,146 @@
// Copyright (C) 2012 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.
//
// Author: Patrick Mezard
//
// Basic test cases for MappingFileProvider.
#include "phonenumbers/geocoding/area_code_map.h"
#include <cstddef>
#include <vector>
#include <gtest/gtest.h> // NOLINT(build/include_order)
#include "phonenumbers/phonenumber.pb.h"
namespace i18n {
namespace phonenumbers {
using std::map;
using std::string;
using std::vector;
namespace {
void MakeCodeMap(const map<int, string>& m, scoped_ptr<AreaCodeMap>* code_map) {
scoped_ptr<AreaCodeMap> cm(new AreaCodeMap());
cm->ReadAreaCodeMap(m);
code_map->swap(cm);
}
void MakeCodeMapUS(scoped_ptr<AreaCodeMap>* code_map) {
map<int, string> m;
m[1212] = "New York";
m[1480] = "Arizona";
m[1650] = "California";
m[1907] = "Alaska";
m[1201664] = "Westwood, NJ";
m[1480893] = "Phoenix, AZ";
m[1501372] = "Little Rock, AR";
m[1626308] = "Alhambra, CA";
m[1650345] = "San Mateo, CA";
m[1867993] = "Dawson, YT";
m[1972480] = "Richardson, TX";
MakeCodeMap(m, code_map);
}
void MakeCodeMapIT(scoped_ptr<AreaCodeMap>* code_map) {
map<int, string> m;
m[3902] = "Milan";
m[3906] = "Rome";
m[39010] = "Genoa";
m[390131] = "Alessandria";
m[390321] = "Novara";
m[390975] = "Potenza";
MakeCodeMap(m, code_map);
}
PhoneNumber MakePhoneNumber(int32 country_code, uint64 national_number) {
PhoneNumber number;
number.set_country_code(country_code);
number.set_national_number(national_number);
return number;
}
} // namespace
class AreaCodeMapTest : public testing::Test {
protected:
virtual void SetUp() {
MakeCodeMapUS(&map_US_);
MakeCodeMapIT(&map_IT_);
}
scoped_ptr<AreaCodeMap> map_US_;
scoped_ptr<AreaCodeMap> map_IT_;
};
TEST_F(AreaCodeMapTest, TestLookupInvalidNumberUS) {
EXPECT_EQ("New York", *map_US_->Lookup(MakePhoneNumber(1, 2121234567L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberNJ) {
EXPECT_EQ("Westwood, NJ", *map_US_->Lookup(MakePhoneNumber(1, 2016641234L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberNY) {
EXPECT_EQ("New York", *map_US_->Lookup(MakePhoneNumber(1, 2126641234L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberCA1) {
EXPECT_EQ("San Mateo, CA", *map_US_->Lookup(MakePhoneNumber(1, 6503451234L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberCA2) {
EXPECT_EQ("California", *map_US_->Lookup(MakePhoneNumber(1, 6502531234L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberTX) {
EXPECT_EQ("Richardson, TX",
*map_US_->Lookup(MakePhoneNumber(1, 9724801234L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberNotFoundTX) {
EXPECT_EQ(NULL, map_US_->Lookup(MakePhoneNumber(1, 9724811234L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberCH) {
EXPECT_EQ(NULL, map_US_->Lookup(MakePhoneNumber(41, 446681300L)));
}
TEST_F(AreaCodeMapTest, TestLookupNumberIT) {
PhoneNumber number = MakePhoneNumber(39, 212345678L);
number.set_italian_leading_zero(true);
EXPECT_EQ("Milan", *map_IT_->Lookup(number));
number.set_national_number(612345678L);
EXPECT_EQ("Rome", *map_IT_->Lookup(number));
number.set_national_number(3211234L);
EXPECT_EQ("Novara", *map_IT_->Lookup(number));
// A mobile number
number.set_national_number(321123456L);
number.set_italian_leading_zero(false);
EXPECT_EQ(NULL, map_IT_->Lookup(number));
// An invalid number (too short)
number.set_national_number(321123L);
number.set_italian_leading_zero(true);
EXPECT_EQ("Novara", *map_IT_->Lookup(number));
}
} // namespace phonenumbers
} // namespace i18n

Loading…
Cancel
Save