diff options
author | Android Chromium Automerger <chromium-automerger@android> | 2014-06-17 03:33:39 +0000 |
---|---|---|
committer | Android Chromium Automerger <chromium-automerger@android> | 2014-06-17 03:33:39 +0000 |
commit | d56141c71497da3224256e3d007f4ab1b8bf4ed4 (patch) | |
tree | 6b0965940e7059bb96891e00e9fe8da7a8383c33 | |
parent | 4e065f4283db7f3b540a0d89857ef87d8400da35 (diff) | |
parent | 128ae075bbf50996c1068740746f27430f89136d (diff) | |
download | src-d56141c71497da3224256e3d007f4ab1b8bf4ed4.tar.gz |
Merge third_party/libaddressinput/src from https://chromium.googlesource.com/external/libaddressinput.git at 128ae075bbf50996c1068740746f27430f89136d
This commit was generated by merge_from_chromium.py.
Change-Id: I2f62550bd34863534a2ad5e229f931fa6aec7546
42 files changed, 271 insertions, 170 deletions
diff --git a/codereview.settings b/codereview.settings new file mode 100644 index 0000000..efe4f4e --- /dev/null +++ b/codereview.settings @@ -0,0 +1,4 @@ +# This file is used by gcl to get repository specific information. +CODE_REVIEW_SERVER: codereview.appspot.com +TRY_ON_UPLOAD: False +VIEW_VC: http://code.google.com/p/libaddressinput/source/detail?r= diff --git a/cpp/include/libaddressinput/address_formatter.h b/cpp/include/libaddressinput/address_formatter.h index c6dc485..e267884 100644 --- a/cpp/include/libaddressinput/address_formatter.h +++ b/cpp/include/libaddressinput/address_formatter.h @@ -13,6 +13,10 @@ // limitations under the License. // // Utility functions for formatting the addresses represented as AddressData. +// +// Note these work best if the address has a language code specified - this can +// be obtained when building the UI components (calling BuildComponents on +// address_ui.h). #ifndef I18N_ADDRESSINPUT_ADDRESS_FORMATTER_H_ #define I18N_ADDRESSINPUT_ADDRESS_FORMATTER_H_ diff --git a/cpp/include/libaddressinput/address_validator.h b/cpp/include/libaddressinput/address_validator.h index bd8d4e9..d3a09f3 100644 --- a/cpp/include/libaddressinput/address_validator.h +++ b/cpp/include/libaddressinput/address_validator.h @@ -66,7 +66,8 @@ typedef std::multimap<AddressField, AddressProblem> FieldProblemMap; // }; class AddressValidator { public: - typedef i18n::addressinput::Callback<AddressData, FieldProblemMap> Callback; + typedef i18n::addressinput::Callback<const AddressData&, + const FieldProblemMap&> Callback; // Does not take ownership of |supplier|. AddressValidator(Supplier* supplier); diff --git a/cpp/include/libaddressinput/callback.h b/cpp/include/libaddressinput/callback.h index 681c087..d8c4ea6 100644 --- a/cpp/include/libaddressinput/callback.h +++ b/cpp/include/libaddressinput/callback.h @@ -15,9 +15,7 @@ // An object to store a pointer to a method in an object with the following // signature: // -// void Observer::ObserveEvent(bool success, -// const Key& key, -// const Data& data); +// void Observer::ObserveEvent(bool success, Key key, Data data); #ifndef I18N_ADDRESSINPUT_CALLBACK_H_ #define I18N_ADDRESSINPUT_CALLBACK_H_ @@ -31,7 +29,7 @@ namespace addressinput { // Stores a pointer to a method in an object. Sample usage: // class MyClass { // public: -// typedef Callback<MyType, MyDataType> MyCallback; +// typedef Callback<const MyType&, const MyDataType&> MyCallback; // // void GetDataAsynchronously() { // scoped_ptr<MyCallback> callback(BuildCallback( @@ -52,10 +50,7 @@ template <typename Key, typename Data> class Callback { public: virtual ~Callback() {} - - virtual void operator()(bool success, - const Key& key, - const Data& data) const = 0; + virtual void operator()(bool success, Key key, Data data) const = 0; }; namespace { @@ -63,7 +58,7 @@ namespace { template <typename Observer, typename Key, typename Data> class CallbackImpl : public Callback<Key, Data> { public: - typedef void (Observer::*ObserveEvent)(bool, const Key&, const Data&); + typedef void (Observer::*ObserveEvent)(bool, Key, Data); CallbackImpl(Observer* observer, ObserveEvent observe_event) : observer_(observer), @@ -74,9 +69,7 @@ class CallbackImpl : public Callback<Key, Data> { virtual ~CallbackImpl() {} - virtual void operator()(bool success, - const Key& key, - const Data& data) const { + virtual void operator()(bool success, Key key, Data data) const { (observer_->*observe_event_)(success, key, data); } @@ -92,7 +85,7 @@ class CallbackImpl : public Callback<Key, Data> { template <typename Observer, typename Key, typename Data> Callback<Key, Data>* BuildCallback( Observer* observer, - void (Observer::*observe_event)(bool, const Key&, const Data&)) { + void (Observer::*observe_event)(bool, Key, Data)) { return new CallbackImpl<Observer, Key, Data>(observer, observe_event); } diff --git a/cpp/include/libaddressinput/downloader.h b/cpp/include/libaddressinput/downloader.h index 6614635..0da4e1f 100644 --- a/cpp/include/libaddressinput/downloader.h +++ b/cpp/include/libaddressinput/downloader.h @@ -25,19 +25,22 @@ namespace i18n { namespace addressinput { -// Downloads validation rules from the server. Sample usage: +// Downloads validation rules from the server. The downloaded data must be +// allocated on the heap, passing ownership to the callback. Sample usage: +// // class MyDownloader : public Downloader { // public: // virtual void Download(const std::string& url, // const Callback& downloaded) const { // bool success = ... -// std::string data = ... +// std::string* data = new ... // downloaded(success, url, data); // } // }; class Downloader { public: - typedef i18n::addressinput::Callback<std::string, std::string> Callback; + typedef i18n::addressinput::Callback<const std::string&, + std::string*> Callback; virtual ~Downloader() {} diff --git a/cpp/include/libaddressinput/null_storage.h b/cpp/include/libaddressinput/null_storage.h index 82d40a8..66d9ab5 100644 --- a/cpp/include/libaddressinput/null_storage.h +++ b/cpp/include/libaddressinput/null_storage.h @@ -33,7 +33,7 @@ class NullStorage : public Storage { virtual ~NullStorage(); // No-op. - virtual void Put(const std::string& key, const std::string& data); + virtual void Put(const std::string& key, std::string* data); // Always calls the |data_ready| callback function signalling failure. virtual void Get(const std::string& key, const Callback& data_ready) const; diff --git a/cpp/include/libaddressinput/preload_supplier.h b/cpp/include/libaddressinput/preload_supplier.h index 447911f..f7654ab 100644 --- a/cpp/include/libaddressinput/preload_supplier.h +++ b/cpp/include/libaddressinput/preload_supplier.h @@ -48,7 +48,7 @@ class Storage; // in total less than 2 MB of JSON data.) class PreloadSupplier : public Supplier { public: - typedef i18n::addressinput::Callback<std::string, int> Callback; + typedef i18n::addressinput::Callback<const std::string&, int> Callback; // Takes ownership of |downloader| and |storage|. The |validation_data_url| // should be a URL to a service that returns address metadata aggregated per diff --git a/cpp/include/libaddressinput/storage.h b/cpp/include/libaddressinput/storage.h index eb06445..38fb887 100644 --- a/cpp/include/libaddressinput/storage.h +++ b/cpp/include/libaddressinput/storage.h @@ -25,28 +25,33 @@ namespace i18n { namespace addressinput { -// Stores downloaded validation rules. Sample usage: +// Stores downloaded validation rules. The data must be allocated on the heap, +// passing ownership to the called function. Sample usage: +// // class MyStorage : public Storage { // public: -// virtual void Put(const std::string& key, const std::string& data) { +// virtual void Put(const std::string& key, std::string* data) { // ... +// delete data; // } // // virtual void Get(const std::string& key, // const Callback& data_ready) const { // bool success = ... -// std::string data = ... +// std::string* data = new ... // data_ready(success, key, data); // } // }; class Storage { public: - typedef i18n::addressinput::Callback<std::string, std::string> Callback; + typedef i18n::addressinput::Callback<const std::string&, + std::string*> Callback; virtual ~Storage() {} - // Stores |data| for |key|. - virtual void Put(const std::string& key, const std::string& data) = 0; + // Stores |data| for |key|, where |data| is an object allocated on the heap, + // which Storage takes ownership of. + virtual void Put(const std::string& key, std::string* data) = 0; // Retrieves the data for |key| and invokes the |data_ready| callback. virtual void Get(const std::string& key, diff --git a/cpp/include/libaddressinput/supplier.h b/cpp/include/libaddressinput/supplier.h index 8f67550..fcd64ad 100644 --- a/cpp/include/libaddressinput/supplier.h +++ b/cpp/include/libaddressinput/supplier.h @@ -29,7 +29,8 @@ class Rule; class Supplier { public: struct RuleHierarchy; - typedef i18n::addressinput::Callback<LookupKey, RuleHierarchy> Callback; + typedef i18n::addressinput::Callback<const LookupKey&, + const RuleHierarchy&> Callback; virtual ~Supplier() {} diff --git a/cpp/src/address_input_helper.cc b/cpp/src/address_input_helper.cc index 91eacf8..a8ce92f 100644 --- a/cpp/src/address_input_helper.cc +++ b/cpp/src/address_input_helper.cc @@ -45,14 +45,6 @@ const char kLookupKeySeparator = '/'; const size_t kHierarchyDepth = arraysize(LookupKey::kHierarchy); -// Determines whether a given string is a reg-exp or a string. We consider a -// string to be anything that doesn't contain characters with special meanings -// in regular expressions - (, [, \, {, ?. These special characters are all the -// ones that appear in the postal code regular expressions. -bool ContainsRegExSpecialCharacters(const std::string& input) { - return input.find_first_of("([\\{?") != std::string::npos; -} - // Gets the best name for the entity represented by the current rule, using the // language provided. The language is currently used to distinguish whether a // Latin-script name should be fetched; if it is not explicitly Latin-script, we @@ -133,14 +125,7 @@ void AddressInputHelper::FillAddress(AddressData* address) const { const RE2ptr* postal_code_reg_exp = region_rule->GetPostalCodeMatcher(); if (postal_code_reg_exp != NULL) { if (address->postal_code.empty()) { - // Every reg-exp has been anchored at the start so we can use PartialMatch - // for prefix matching; we have to remove this here. - // TODO: Modify the rule class to store and expose this directly to make - // this more robust. - std::string pattern(postal_code_reg_exp->ptr->pattern().substr(1)); - if (!ContainsRegExSpecialCharacters(pattern)) { - address->postal_code = pattern; - } + address->postal_code = region_rule->GetSolePostalCode(); } // If we have a valid postal code, try and work out the most specific diff --git a/cpp/src/null_storage.cc b/cpp/src/null_storage.cc index 1f0e999..7786a0d 100644 --- a/cpp/src/null_storage.cc +++ b/cpp/src/null_storage.cc @@ -14,6 +14,8 @@ #include <libaddressinput/null_storage.h> +#include <cassert> +#include <cstddef> #include <string> namespace i18n { @@ -25,12 +27,14 @@ NullStorage::NullStorage() { NullStorage::~NullStorage() { } -void NullStorage::Put(const std::string& key, const std::string& data) { +void NullStorage::Put(const std::string& key, std::string* data) { + assert(data != NULL); // Sanity check. + delete data; } void NullStorage::Get(const std::string& key, const Callback& data_ready) const { - data_ready(false, key, std::string()); + data_ready(false, key, NULL); } } // namespace addressinput diff --git a/cpp/src/region_data_constants.cc b/cpp/src/region_data_constants.cc index 0d88e08..f3d9c3c 100644 --- a/cpp/src/region_data_constants.cc +++ b/cpp/src/region_data_constants.cc @@ -921,7 +921,7 @@ std::map<std::string, std::string> InitRegionData() { "\"fmt\":\"%O%n%N%n%A%n%Z %C\"," "\"require\":\"ACZ\"," "\"zipex\":\"1234 AB,2490 AA\"," - "\"posturl\":\"http://www.tntpost.nl/voorthuis/\"," + "\"posturl\":\"http://www.postnl.nl/voorthuis/\"," "\"languages\":\"nl\"" "}")); region_data.insert(std::make_pair("NO", "{" diff --git a/cpp/src/retriever.cc b/cpp/src/retriever.cc index 967face..8fa76b7 100644 --- a/cpp/src/retriever.cc +++ b/cpp/src/retriever.cc @@ -57,27 +57,29 @@ class Helper { void OnValidatedDataReady(bool success, const std::string& key, - const std::string& data) { + std::string* data) { if (success) { - retrieved_(success, key, data); + assert(data != NULL); + retrieved_(success, key, *data); delete this; } else { // Validating storage returns (false, key, stale-data) for valid but stale // data. If |data| is empty, however, then it's either missing or invalid. - if (!data.empty()) { - stale_data_ = data; + if (data != NULL && !data->empty()) { + stale_data_ = *data; } downloader_.Download(lookup_key_util_.GetUrlForKey(key), *downloaded_); } + delete data; } - void OnDownloaded(bool success, - const std::string& url, - const std::string& data) { + void OnDownloaded(bool success, const std::string& url, std::string* data) { const std::string& key = lookup_key_util_.GetKeyForUrl(url); if (success) { + assert(data != NULL); + retrieved_(true, key, *data); storage_->Put(key, data); - retrieved_(true, key, data); + data = NULL; // Deleted by Storage::Put(). } else if (!stale_data_.empty()) { // Reuse the stale data if a download fails. It's better to have slightly // outdated validation rules than to suddenly lose validation ability. @@ -85,6 +87,7 @@ class Helper { } else { retrieved_(false, key, std::string()); } + delete data; delete this; } diff --git a/cpp/src/retriever.h b/cpp/src/retriever.h index f3f7e18..17d17ec 100644 --- a/cpp/src/retriever.h +++ b/cpp/src/retriever.h @@ -42,7 +42,8 @@ class ValidatingStorage; // retriever.Retrieve("data/CA/AB--fr", *retrieved); class Retriever { public: - typedef i18n::addressinput::Callback<std::string, std::string> Callback; + typedef i18n::addressinput::Callback<const std::string&, + const std::string&> Callback; // Takes ownership of |downloader| and |storage|. Retriever(const std::string& validation_data_url, diff --git a/cpp/src/rule.cc b/cpp/src/rule.cc index 8a47239..e25142f 100644 --- a/cpp/src/rule.cc +++ b/cpp/src/rule.cc @@ -109,6 +109,14 @@ int GetMessageIdFromName(const std::string& name, return it != message_ids.end() ? it->second : INVALID_MESSAGE_ID; } +// Determines whether a given string is a reg-exp or a string. We consider a +// string to be anything that doesn't contain characters with special meanings +// in regular expressions - (, [, \, {, ?. These special characters are all the +// ones that appear in the postal code regular expressions. +bool ContainsRegExSpecialCharacters(const std::string& input) { + return input.find_first_of("([\\{?") != std::string::npos; +} + } // namespace Rule::Rule() @@ -119,6 +127,7 @@ Rule::Rule() sub_keys_(), languages_(), postal_code_matcher_(NULL), + sole_postal_code_(), admin_area_name_message_id_(INVALID_MESSAGE_ID), postal_code_name_message_id_(INVALID_MESSAGE_ID), name_(), @@ -152,6 +161,7 @@ void Rule::CopyFrom(const Rule& rule) { ? NULL : new RE2ptr(new RE2(rule.postal_code_matcher_->ptr->pattern(), rule.postal_code_matcher_->ptr->options()))); + sole_postal_code_ = rule.sole_postal_code_; admin_area_name_message_id_ = rule.admin_area_name_message_id_; postal_code_name_message_id_ = rule.postal_code_name_message_id_; name_ = rule.name_; @@ -197,6 +207,7 @@ void Rule::ParseJsonRule(const Json& json) { json.GetStringValueForKey(kLanguagesKey), kSeparator, &languages_); } + sole_postal_code_.clear(); if (json.HasStringValueForKey(kZipKey)) { const std::string& zip = json.GetStringValueForKey(kZipKey); // The "zip" field in the JSON data is used in two different ways to @@ -212,13 +223,18 @@ void Rule::ParseJsonRule(const Json& json) { // RE2::FullMatch() to perform matching against the entire string. RE2::Options options; options.set_never_capture(true); - RE2* matcher = new RE2("^" + zip, options); + RE2* matcher = new RE2("^(" + zip + ")", options); if (matcher->ok()) { postal_code_matcher_.reset(new RE2ptr(matcher)); } else { postal_code_matcher_.reset(NULL); delete matcher; } + // If the "zip" field is not a regular expression, then it is the sole + // postal code for this rule. + if (!ContainsRegExSpecialCharacters(zip)) { + sole_postal_code_ = zip; + } } if (json.HasStringValueForKey(kAdminAreaNameTypeKey)) { diff --git a/cpp/src/rule.h b/cpp/src/rule.h index 4954b98..c722932 100644 --- a/cpp/src/rule.h +++ b/cpp/src/rule.h @@ -98,6 +98,11 @@ class Rule { return postal_code_matcher_.get(); } + // Returns the sole postal code for this rule, if there is one. + const std::string& GetSolePostalCode() const { + return sole_postal_code_; + } + // The message string identifier for admin area name. If not set, then // INVALID_MESSAGE_ID. int GetAdminAreaNameMessageId() const { return admin_area_name_message_id_; } @@ -134,6 +139,7 @@ class Rule { std::vector<std::string> sub_keys_; std::vector<std::string> languages_; scoped_ptr<const RE2ptr> postal_code_matcher_; + std::string sole_postal_code_; int admin_area_name_message_id_; int postal_code_name_message_id_; std::string name_; diff --git a/cpp/src/rule_retriever.h b/cpp/src/rule_retriever.h index a44fb63..ba3b4c7 100644 --- a/cpp/src/rule_retriever.h +++ b/cpp/src/rule_retriever.h @@ -37,7 +37,8 @@ class Rule; // rules.RetrieveRule("data/CA/AB--fr", *rule_ready); class RuleRetriever { public: - typedef i18n::addressinput::Callback<std::string, Rule> Callback; + typedef i18n::addressinput::Callback<const std::string&, + const Rule&> Callback; // Takes ownership of |retriever|. explicit RuleRetriever(const Retriever* retriever); diff --git a/cpp/src/validating_storage.cc b/cpp/src/validating_storage.cc index a598126..7dc231c 100644 --- a/cpp/src/validating_storage.cc +++ b/cpp/src/validating_storage.cc @@ -49,17 +49,21 @@ class Helper { void OnWrappedDataReady(bool success, const std::string& key, - const std::string& wrapped_data) { - std::string data(wrapped_data); + std::string* data) { if (success) { - bool is_stale = !ValidatingUtil::UnwrapTimestamp(&data, time(NULL)); - bool is_corrupted = !ValidatingUtil::UnwrapChecksum(&data); - data_ready_(!is_corrupted && !is_stale, - key, - is_corrupted ? std::string() : data); + assert(data != NULL); + bool is_stale = !ValidatingUtil::UnwrapTimestamp(data, time(NULL)); + bool is_corrupted = !ValidatingUtil::UnwrapChecksum(data); + success = !is_corrupted && !is_stale; + if (is_corrupted) { + delete data; + data = NULL; + } } else { - data_ready_(false, key, std::string()); + delete data; + data = NULL; } + data_ready_(success, key, data); delete this; } @@ -78,8 +82,10 @@ ValidatingStorage::ValidatingStorage(Storage* storage) ValidatingStorage::~ValidatingStorage() {} -void ValidatingStorage::Put(const std::string& key, const std::string& data) { - wrapped_storage_->Put(key, ValidatingUtil::Wrap(data, time(NULL))); +void ValidatingStorage::Put(const std::string& key, std::string* data) { + assert(data != NULL); + ValidatingUtil::Wrap(time(NULL), data); + wrapped_storage_->Put(key, data); } void ValidatingStorage::Get(const std::string& key, diff --git a/cpp/src/validating_storage.h b/cpp/src/validating_storage.h index 272e30c..11e74cd 100644 --- a/cpp/src/validating_storage.h +++ b/cpp/src/validating_storage.h @@ -31,7 +31,7 @@ namespace addressinput { // Wraps Storage to add checksum and timestamp to stored data. Sample usage: // scoped_ptr<Storage> file_storage = ...; // ValidatingStorage storage(file_storage)); -// storage.Put("key", "data"); +// storage.Put("key", new std::string("data")); // scoped_ptr<ValidatingStorage::Callback> data_ready(BuildCallback( // this, &MyClass::OnDataReady)); // storage.Get("key", *data_ready); @@ -42,7 +42,7 @@ class ValidatingStorage : public Storage { virtual ~ValidatingStorage(); // Storage implementation. - virtual void Put(const std::string& key, const std::string& data); + virtual void Put(const std::string& key, std::string* data); // Storage implementation. // If the data is invalid, then |data_ready| will be called with (false, key, diff --git a/cpp/src/validating_util.cc b/cpp/src/validating_util.cc index de43f80..6b52e8b 100644 --- a/cpp/src/validating_util.cc +++ b/cpp/src/validating_util.cc @@ -81,21 +81,22 @@ bool UnwrapHeader(const char* header_prefix, } // namespace // static -std::string ValidatingUtil::Wrap(const std::string& data, time_t timestamp) { +void ValidatingUtil::Wrap(time_t timestamp, std::string* data) { + assert(data != NULL); char timestamp_string[2 + 3 * sizeof timestamp]; snprintf(timestamp_string, sizeof timestamp_string, "%ld", timestamp); - std::string wrapped; - wrapped.append(kTimestampPrefix, kTimestampPrefixLength); - wrapped.append(timestamp_string); - wrapped.push_back(kSeparator); + std::string header; + header.append(kTimestampPrefix, kTimestampPrefixLength); + header.append(timestamp_string); + header.push_back(kSeparator); - wrapped.append(kChecksumPrefix, kChecksumPrefixLength); - wrapped.append(MD5String(data)); - wrapped.push_back(kSeparator); - wrapped.append(data); + header.append(kChecksumPrefix, kChecksumPrefixLength); + header.append(MD5String(*data)); + header.push_back(kSeparator); - return wrapped; + data->reserve(header.size() + data->size()); + data->insert(0, header); } // static diff --git a/cpp/src/validating_util.h b/cpp/src/validating_util.h index 8a9b532..1357651 100644 --- a/cpp/src/validating_util.h +++ b/cpp/src/validating_util.h @@ -26,9 +26,9 @@ namespace i18n { namespace addressinput { // Wraps data with a checksum and a timestamp. Sample usage: -// std::string data = ... -// std::string wrapped = ValidatingUtil::Wrap(data, time(NULL)); -// Process(wrapped); +// std::string data = ... +// ValidatingUtil::Wrap(time(NULL), &data); +// Process(data); // // std::string unwrapped = wrapped; // if (ValidatingUtil::UnwrapTimestamp(&unwrapped, time(NULL)) && @@ -37,8 +37,8 @@ namespace addressinput { // } class ValidatingUtil { public: - // Returns |data| with attached checksum and given |timestamp|. - static std::string Wrap(const std::string& data, time_t timestamp); + // Adds checksum and given |timestamp| to |data|. + static void Wrap(time_t timestamp, std::string* data); // Strips out the timestamp from |data|. Returns |true| if the timestamp is // present, formatted correctly, valid, and recent with respect to |now|. diff --git a/cpp/test/address_input_helper_test.cc b/cpp/test/address_input_helper_test.cc index ebb0fa1..84a2286 100644 --- a/cpp/test/address_input_helper_test.cc +++ b/cpp/test/address_input_helper_test.cc @@ -65,7 +65,7 @@ class AddressInputHelperTest : public testing::Test { private: // Used to preload data that we need. - void Loaded(bool success, const std::string&, const int&) { + void Loaded(bool success, const std::string&, int) { ASSERT_TRUE(success); } @@ -271,7 +271,7 @@ class AddressInputHelperMockDataTest : public testing::Test { private: // Our mock downloader we assume will always succeed. - void Loaded(bool success, const std::string&, const int&) { + void Loaded(bool success, const std::string&, int) { ASSERT_TRUE(success); } diff --git a/cpp/test/address_normalizer_test.cc b/cpp/test/address_normalizer_test.cc index d859250..6f2d9f4 100644 --- a/cpp/test/address_normalizer_test.cc +++ b/cpp/test/address_normalizer_test.cc @@ -51,9 +51,7 @@ class AddressNormalizerTest : public testing::Test { const AddressNormalizer normalizer_; private: - void OnLoaded(bool success, - const std::string& region_code, - const int& num_rules) { + void OnLoaded(bool success, const std::string& region_code, int num_rules) { ASSERT_TRUE(success); ASSERT_FALSE(region_code.empty()); ASSERT_LT(0, num_rules); diff --git a/cpp/test/address_validator_test.cc b/cpp/test/address_validator_test.cc index 20ede92..5250502 100644 --- a/cpp/test/address_validator_test.cc +++ b/cpp/test/address_validator_test.cc @@ -131,7 +131,7 @@ class PreloadValidatorWrapper : public ValidatorWrapper { validator_(&supplier_), loaded_(BuildCallback(this, &PreloadValidatorWrapper::Loaded)) {} - void Loaded(bool success, const std::string&, const int&) { + void Loaded(bool success, const std::string&, int) { ASSERT_TRUE(success); } diff --git a/cpp/test/fake_downloader.cc b/cpp/test/fake_downloader.cc index cf54cf7..0f3f8f1 100644 --- a/cpp/test/fake_downloader.cc +++ b/cpp/test/fake_downloader.cc @@ -158,17 +158,18 @@ void FakeDownloader::Download(const std::string& url, std::map<std::string, std::string>::const_iterator data_it = GetData().find(url); bool success = data_it != GetData().end(); - std::string data = success ? data_it->second : std::string(); - if (!success && - (GetLookupKeyUtil().IsValidationDataUrl(url) || - GetAggregateLookupKeyUtil().IsValidationDataUrl(url))) { + std::string* data = NULL; + if (success) { + data = new std::string(data_it->second); + } else if (GetLookupKeyUtil().IsValidationDataUrl(url) || + GetAggregateLookupKeyUtil().IsValidationDataUrl(url)) { // URLs that start with "https://i18napis.appspot.com/ssl-address/" or // "https://i18napis.appspot.com/ssl-aggregate-address/" prefix, but do not // have associated data will always return "{}" with status code 200. // FakeDownloader imitates this behavior for URLs that start with // "test://address/" and "test://aggregate-address/" prefixes. success = true; - data = "{}"; + data = new std::string("{}"); } downloaded(success, url, data); } diff --git a/cpp/test/fake_downloader.h b/cpp/test/fake_downloader.h index 2dde442..111ce7a 100644 --- a/cpp/test/fake_downloader.h +++ b/cpp/test/fake_downloader.h @@ -41,8 +41,9 @@ namespace addressinput { // private: // void OnDownloaded(bool success, // const std::string& url, -// const std::string& data) { +// std::string* data) { // ... +// delete data; // } // // FakeDownloader downloader_; diff --git a/cpp/test/fake_downloader_test.cc b/cpp/test/fake_downloader_test.cc index 477a462..2d88acc 100644 --- a/cpp/test/fake_downloader_test.cc +++ b/cpp/test/fake_downloader_test.cc @@ -18,6 +18,7 @@ #include <libaddressinput/downloader.h> #include <libaddressinput/util/scoped_ptr.h> +#include <cstddef> #include <string> #include <gtest/gtest.h> @@ -48,12 +49,14 @@ class FakeDownloaderTest : public testing::TestWithParam<std::string> { std::string data_; private: - void OnDownloaded(bool success, - const std::string& url, - const std::string& data) { + void OnDownloaded(bool success, const std::string& url, std::string* data) { + ASSERT_FALSE(success && data == NULL); success_ = success; url_ = url; - data_ = data; + if (data != NULL) { + data_ = *data; + delete data; + } } }; diff --git a/cpp/test/fake_storage.cc b/cpp/test/fake_storage.cc index 0fd8f62..7ba96c8 100644 --- a/cpp/test/fake_storage.cc +++ b/cpp/test/fake_storage.cc @@ -14,26 +14,40 @@ #include "fake_storage.h" +#include <cassert> +#include <cstddef> #include <map> #include <string> +#include <utility> namespace i18n { namespace addressinput { FakeStorage::FakeStorage() {} -FakeStorage::~FakeStorage() {} +FakeStorage::~FakeStorage() { + for (std::map<std::string, std::string*>::const_iterator + it = data_.begin(); it != data_.end(); ++it) { + delete it->second; + } +} -void FakeStorage::Put(const std::string& key, const std::string& data) { - data_[key] = data; +void FakeStorage::Put(const std::string& key, std::string* data) { + assert(data != NULL); + std::pair<std::map<std::string, std::string*>::iterator, bool> result = + data_.insert(std::make_pair(key, data)); + if (!result.second) { + // Replace data in existing entry for this key. + delete result.first->second; + result.first->second = data; + } } void FakeStorage::Get(const std::string& key, const Callback& data_ready) const { - std::map<std::string, std::string>::const_iterator data_it = data_.find(key); + std::map<std::string, std::string*>::const_iterator data_it = data_.find(key); bool success = data_it != data_.end(); - std::string data = success ? data_it->second : std::string(); - data_ready(success, key, data); + data_ready(success, key, success ? new std::string(*data_it->second) : NULL); } } // namespace addressinput diff --git a/cpp/test/fake_storage.h b/cpp/test/fake_storage.h index e14c443..5e07cb0 100644 --- a/cpp/test/fake_storage.h +++ b/cpp/test/fake_storage.h @@ -45,8 +45,9 @@ namespace addressinput { // private: // void OnDataReady(bool success, // const std::string& key, -// const std::string& data) { +// std::string* data) { // ... +// delete data; // } // // FakeStorage storage_; @@ -60,11 +61,11 @@ class FakeStorage : public Storage { virtual ~FakeStorage(); // Storage implementation. - virtual void Put(const std::string& key, const std::string& data); + virtual void Put(const std::string& key, std::string* data); virtual void Get(const std::string& key, const Callback& data_ready) const; private: - std::map<std::string, std::string> data_; + std::map<std::string, std::string*> data_; }; } // namespace addressinput diff --git a/cpp/test/fake_storage_test.cc b/cpp/test/fake_storage_test.cc index 516d342..49663ca 100644 --- a/cpp/test/fake_storage_test.cc +++ b/cpp/test/fake_storage_test.cc @@ -18,6 +18,7 @@ #include <libaddressinput/storage.h> #include <libaddressinput/util/scoped_ptr.h> +#include <cstddef> #include <string> #include <gtest/gtest.h> @@ -47,10 +48,14 @@ class FakeStorageTest : public testing::Test { private: void OnDataReady(bool success, const std::string& key, - const std::string& data) { + std::string* data) { + ASSERT_FALSE(success && data == NULL); success_ = success; key_ = key; - data_ = data; + if (data != NULL) { + data_ = *data; + delete data; + } } }; @@ -64,7 +69,7 @@ TEST_F(FakeStorageTest, GetWithoutPutReturnsEmptyData) { } TEST_F(FakeStorageTest, GetReturnsWhatWasPut) { - storage_.Put("key", "value"); + storage_.Put("key", new std::string("value")); scoped_ptr<Storage::Callback> callback(BuildCallback()); storage_.Get("key", *callback); @@ -75,8 +80,8 @@ TEST_F(FakeStorageTest, GetReturnsWhatWasPut) { } TEST_F(FakeStorageTest, SecondPutOverwritesData) { - storage_.Put("key", "bad-value"); - storage_.Put("key", "good-value"); + storage_.Put("key", new std::string("bad-value")); + storage_.Put("key", new std::string("good-value")); scoped_ptr<Storage::Callback> callback(BuildCallback()); storage_.Get("key", *callback); diff --git a/cpp/test/mock_downloader.cc b/cpp/test/mock_downloader.cc index 3158b3e..71256aa 100644 --- a/cpp/test/mock_downloader.cc +++ b/cpp/test/mock_downloader.cc @@ -15,6 +15,7 @@ #include "mock_downloader.h" #include <cassert> +#include <cstddef> #include <map> #include <string> @@ -41,7 +42,7 @@ void MockDownloader::Download(const std::string& url, std::string key(url, kMockDataUrlLength); std::map<std::string, std::string>::const_iterator it = data_.find(key); bool success = it != data_.end(); - downloaded(success, url, success ? it->second : std::string()); + downloaded(success, url, success ? new std::string(it->second) : NULL); } } // namespace addressinput diff --git a/cpp/test/mock_downloader.h b/cpp/test/mock_downloader.h index 128942e..b423660 100644 --- a/cpp/test/mock_downloader.h +++ b/cpp/test/mock_downloader.h @@ -46,8 +46,9 @@ namespace addressinput { // private: // void OnDownloaded(bool success, // const std::string& url, -// const std::string& data) { +// std::string* data) { // ... +// delete data; // } // // MockDownloader downloader_; diff --git a/cpp/test/null_storage_test.cc b/cpp/test/null_storage_test.cc index c55571d..dca0803 100644 --- a/cpp/test/null_storage_test.cc +++ b/cpp/test/null_storage_test.cc @@ -18,6 +18,7 @@ #include <libaddressinput/util/basictypes.h> #include <libaddressinput/util/scoped_ptr.h> +#include <cstddef> #include <string> #include <gtest/gtest.h> @@ -45,12 +46,14 @@ class NullStorageTest : public testing::Test { static const char kKey[]; private: - void OnDataReady(bool success, - const std::string& key, - const std::string& data) { + void OnDataReady(bool success, const std::string& key, std::string* data) { + ASSERT_FALSE(success && data == NULL); success_ = success; key_ = key; - data_ = data; + if (data != NULL) { + data_ = *data; + delete data; + } } DISALLOW_COPY_AND_ASSIGN(NullStorageTest); @@ -61,7 +64,7 @@ const char NullStorageTest::kKey[] = "foo"; TEST_F(NullStorageTest, Put) { // The Put() method should not do anything, so this test only tests that the // code compiles and that the call doesn't crash. - storage_.Put(kKey, "bar"); + storage_.Put(kKey, new std::string("bar")); } TEST_F(NullStorageTest, Get) { diff --git a/cpp/test/preload_supplier_test.cc b/cpp/test/preload_supplier_test.cc index b812ef1..ab787d8 100644 --- a/cpp/test/preload_supplier_test.cc +++ b/cpp/test/preload_supplier_test.cc @@ -54,9 +54,7 @@ class PreloadSupplierTest : public testing::Test { scoped_ptr<PreloadSupplier::Callback> loaded_callback_; private: - void OnLoaded(bool success, - const std::string& region_code, - const int& num_rules) { + void OnLoaded(bool success, const std::string& region_code, int num_rules) { ASSERT_TRUE(success); ASSERT_FALSE(region_code.empty()); ASSERT_LT(0, num_rules); diff --git a/cpp/test/region_data_builder_test.cc b/cpp/test/region_data_builder_test.cc index 8856630..03b0981 100644 --- a/cpp/test/region_data_builder_test.cc +++ b/cpp/test/region_data_builder_test.cc @@ -55,9 +55,7 @@ class RegionDataBuilderTest : public testing::Test { std::string best_language_; private: - void OnLoaded(bool success, - const std::string& region_code, - const int& num_rules) { + void OnLoaded(bool success, const std::string& region_code, int num_rules) { ASSERT_TRUE(success); ASSERT_FALSE(region_code.empty()); ASSERT_LT(0, num_rules); diff --git a/cpp/test/retriever_test.cc b/cpp/test/retriever_test.cc index e968882..aff418c 100644 --- a/cpp/test/retriever_test.cc +++ b/cpp/test/retriever_test.cc @@ -19,6 +19,7 @@ #include <libaddressinput/storage.h> #include <libaddressinput/util/scoped_ptr.h> +#include <cstddef> #include <string> #include <gtest/gtest.h> @@ -141,11 +142,13 @@ class StaleStorage : public Storage { // Storage implementation. virtual void Get(const std::string& key, const Callback& data_ready) const { - data_ready(true, key, kStaleWrappedData); + data_ready(true, key, new std::string(kStaleWrappedData)); } - virtual void Put(const std::string& key, const std::string& value) { + virtual void Put(const std::string& key, std::string* value) { + ASSERT_TRUE(value != NULL); data_updated_ = true; + delete value; } bool data_updated_; diff --git a/cpp/test/rule_test.cc b/cpp/test/rule_test.cc index 38ac77b..25426a2 100644 --- a/cpp/test/rule_test.cc +++ b/cpp/test/rule_test.cc @@ -105,6 +105,7 @@ TEST(RuleTest, ParseOverwritesRule) { ASSERT_TRUE(rule.ParseSerializedRule("{" "\"fmt\":\"%S%Z\"," "\"state_name_type\":\"area\"," + "\"zip\":\"1234\"," "\"zip_name_type\":\"postal\"," "\"zipex\":\"1234\"," "\"posturl\":\"http://www.testpost.com\"" @@ -114,6 +115,7 @@ TEST(RuleTest, ParseOverwritesRule) { rule.GetAdminAreaNameMessageId()); EXPECT_EQ(IDS_LIBADDRESSINPUT_POSTAL_CODE_LABEL, rule.GetPostalCodeNameMessageId()); + EXPECT_EQ("1234", rule.GetSolePostalCode()); EXPECT_EQ("1234", rule.GetPostalCodeExample()); EXPECT_EQ("http://www.testpost.com", rule.GetPostServiceUrl()); @@ -129,6 +131,7 @@ TEST(RuleTest, ParseOverwritesRule) { rule.GetAdminAreaNameMessageId()); EXPECT_EQ(IDS_LIBADDRESSINPUT_ZIP_CODE_LABEL, rule.GetPostalCodeNameMessageId()); + EXPECT_TRUE(rule.GetSolePostalCode().empty()); EXPECT_EQ("5678", rule.GetPostalCodeExample()); EXPECT_EQ("http://www.fakepost.com", rule.GetPostServiceUrl()); } @@ -339,6 +342,22 @@ TEST_P(RuleParseTest, PostalCodeNameTypeHasUiString) { } } +// Verifies that the sole postal code is correctly recognised and copied. +TEST_P(RuleParseTest, SolePostalCode) { + Rule rule; + ASSERT_TRUE(rule.ParseSerializedRule("{\"zip\":\"1234\"}")); + EXPECT_TRUE(rule.GetPostalCodeMatcher() != NULL); + EXPECT_TRUE(rule.GetSolePostalCode() == "1234"); + + Rule copy; + EXPECT_TRUE(copy.GetPostalCodeMatcher() == NULL); + EXPECT_TRUE(copy.GetSolePostalCode().empty()); + + copy.CopyFrom(rule); + EXPECT_TRUE(copy.GetPostalCodeMatcher() != NULL); + EXPECT_EQ(rule.GetSolePostalCode(), copy.GetSolePostalCode()); +} + // Test parsing all region data. INSTANTIATE_TEST_CASE_P( AllRulesTest, RuleParseTest, diff --git a/cpp/test/supplier_test.cc b/cpp/test/supplier_test.cc index 22a8f7b..13913c3 100644 --- a/cpp/test/supplier_test.cc +++ b/cpp/test/supplier_test.cc @@ -116,7 +116,7 @@ class PreloadSupplierWrapper : public SupplierWrapper { new NullStorage), loaded_(BuildCallback(this, &PreloadSupplierWrapper::Loaded)) {} - void Loaded(bool success, const std::string&, const int&) { + void Loaded(bool success, const std::string&, int) { ASSERT_TRUE(success); } diff --git a/cpp/test/validating_storage_test.cc b/cpp/test/validating_storage_test.cc index 228475c..ced22dc 100644 --- a/cpp/test/validating_storage_test.cc +++ b/cpp/test/validating_storage_test.cc @@ -18,6 +18,7 @@ #include <libaddressinput/storage.h> #include <libaddressinput/util/scoped_ptr.h> +#include <cstddef> #include <string> #include <gtest/gtest.h> @@ -66,17 +67,19 @@ class ValidatingStorageTest : public testing::Test { std::string data_; private: - void OnDataReady(bool success, - const std::string& key, - const std::string& data) { + void OnDataReady(bool success, const std::string& key, std::string* data) { + ASSERT_FALSE(success && data == NULL); success_ = success; key_ = key; - data_ = data; + if (data != NULL) { + data_ = *data; + delete data; + } } }; TEST_F(ValidatingStorageTest, GoodData) { - storage_.Put(kKey, kValidatedData); + storage_.Put(kKey, new std::string(kValidatedData)); scoped_ptr<ValidatingStorage::Callback> callback(BuildCallback()); storage_.Get(kKey, *callback); @@ -87,7 +90,7 @@ TEST_F(ValidatingStorageTest, GoodData) { } TEST_F(ValidatingStorageTest, EmptyData) { - storage_.Put(kKey, kEmptyData); + storage_.Put(kKey, new std::string(kEmptyData)); scoped_ptr<ValidatingStorage::Callback> callback(BuildCallback()); storage_.Get(kKey, *callback); @@ -107,8 +110,8 @@ TEST_F(ValidatingStorageTest, MissingKey) { } TEST_F(ValidatingStorageTest, GarbageData) { - storage_.Put(kKey, kValidatedData); - wrapped_storage_->Put(kKey, "garbage"); + storage_.Put(kKey, new std::string(kValidatedData)); + wrapped_storage_->Put(kKey, new std::string("garbage")); scoped_ptr<ValidatingStorage::Callback> callback(BuildCallback()); storage_.Get(kKey, *callback); @@ -119,8 +122,8 @@ TEST_F(ValidatingStorageTest, GarbageData) { } TEST_F(ValidatingStorageTest, StaleData) { - storage_.Put(kKey, kValidatedData); - wrapped_storage_->Put(kKey, kStaleWrappedData); + storage_.Put(kKey, new std::string(kValidatedData)); + wrapped_storage_->Put(kKey, new std::string(kStaleWrappedData)); scoped_ptr<ValidatingStorage::Callback> callback(BuildCallback()); storage_.Get(kKey, *callback); diff --git a/cpp/test/validating_util_test.cc b/cpp/test/validating_util_test.cc index a255741..b7038d5 100644 --- a/cpp/test/validating_util_test.cc +++ b/cpp/test/validating_util_test.cc @@ -139,11 +139,14 @@ TEST(ValidatingUtilTest, UnwrapTimestamp) { } TEST(ValidatingUtilTest, Wrap) { - EXPECT_EQ(kWrappedData, ValidatingUtil::Wrap(kUnwrappedData, kTimestamp)); + std::string data = kUnwrappedData; + ValidatingUtil::Wrap(kTimestamp, &data); + EXPECT_EQ(kWrappedData, data); } TEST(ValidatingUtilTest, WrapUnwrapIt) { - std::string data = ValidatingUtil::Wrap(kUnwrappedData, kTimestamp); + std::string data = kUnwrappedData; + ValidatingUtil::Wrap(kTimestamp, &data); EXPECT_TRUE(ValidatingUtil::UnwrapTimestamp(&data, kTimestamp)); EXPECT_EQ(kChecksummedData, data); EXPECT_TRUE(ValidatingUtil::UnwrapChecksum(&data)); diff --git a/cpp/test/validation_task_test.cc b/cpp/test/validation_task_test.cc index 076309e..e8136f3 100644 --- a/cpp/test/validation_task_test.cc +++ b/cpp/test/validation_task_test.cc @@ -378,6 +378,20 @@ TEST_F(ValidationTaskTest, PostalCodeMatchingValue) { EXPECT_EQ(expected_, problems_); } +TEST_F(ValidationTaskTest, PostalCodePrefixMismatchingValue) { + json_[0] = "{\"fmt\":\"%Z\",\"zip\":\"\\\\d{5}\"}"; + json_[1] = "{\"zip\":\"9[0-5]|96[01]\"}"; + + address_.region_code = "rrr"; + address_.postal_code = "10960"; + + expected_.insert(std::make_pair(POSTAL_CODE, MISMATCHING_VALUE)); + + ASSERT_NO_FATAL_FAILURE(Validate()); + ASSERT_TRUE(called_); + EXPECT_EQ(expected_, problems_); +} + TEST_F(ValidationTaskTest, PostalCodeFilterIgnoresMismatching) { json_[0] = "{\"zip\":\"\\\\d{3}\"}"; json_[1] = "{\"zip\":\"1\"}"; diff --git a/java/src/com/android/i18n/addressinput/RegionDataConstants.java b/java/src/com/android/i18n/addressinput/RegionDataConstants.java index 2b317df..48affab 100644 --- a/java/src/com/android/i18n/addressinput/RegionDataConstants.java +++ b/java/src/com/android/i18n/addressinput/RegionDataConstants.java @@ -32,24 +32,11 @@ class RegionDataConstants { private static final Map<String, String> COUNTRY_FORMAT_MAP = new HashMap<String, String>(); - /** - * Assumes the array is a well-formed array - i.e., there are no unmatched keys in the input. - * Package-private so it can be accessed by tests. - */ - static String convertArrayToJsonString(String[] input) { - JSONObject object = new JSONObject(); - for (int i = 0; i < input.length; i += 2) { - try { - object.put(input[i], input[i + 1]); - } catch (JSONException e) { - // Ignore for now. - } - } - return object.toString(); - } - private enum RegionDataEnum { + AC(new String[]{ + "name", "ASCENSION ISLAND", + }), AD(new String[]{ "name", "ANDORRA", "lang", "ca", @@ -278,11 +265,9 @@ class RegionDataConstants { "lang", "zh-hans", "languages", "zh-hans", "fmt", "%Z%n%S%C%D%n%A%n%O%n%N", - "lfmt", "%N%n%O%n%A, %D%n%C%n%S, %Z", + "lfmt", "%N%n%O%n%A%n%D%n%C%n%S, %Z", "require", "ACSZ", "upper", "S", - "fmtCharsets", "GB2312", - "hasDisputed", "true", }), CO(new String[]{ "name", "COLOMBIA", @@ -352,7 +337,7 @@ class RegionDataConstants { EG(new String[]{ "name", "EGYPT", "fmt", "%N%n%O%n%A%n%C%n%S%n%Z", - "dir", "rtl", + "lfmt", "%N%n%O%n%A%n%C%n%S%n%Z", }), EH(new String[]{ "name", "WESTERN SAHARA", @@ -503,7 +488,6 @@ class RegionDataConstants { "require", "AS", "upper", "S", "state_name_type", "area", - "fmtCharsets", "Big5", }), HM(new String[]{ "name", "HEARD AND MCDONALD ISLANDS", @@ -544,7 +528,6 @@ class RegionDataConstants { IL(new String[]{ "name", "ISRAEL", "fmt", "%N%n%O%n%A%n%C %Z", - "dir", "rtl", }), IM(new String[]{ "name", "ISLE OF MAN", @@ -611,7 +594,6 @@ class RegionDataConstants { "require", "ACSZ", "upper", "S", "state_name_type", "prefecture", - "fmtCharsets", "ISO-2022-JP", }), KE(new String[]{ "name", "KENYA", @@ -652,7 +634,6 @@ class RegionDataConstants { "require", "ACSZ", "upper", "Z", "state_name_type", "do_si", - "fmtCharsets", "EUC-KR", }), KW(new String[]{ "name", "KUWAIT", @@ -1092,6 +1073,9 @@ class RegionDataConstants { "fmt", "%N%n%O%n%A%n%C%n%Z", "upper", "ACZ", }), + TA(new String[]{ + "name", "TRISTAN DA CUNHA", + }), TC(new String[]{ "name", "TURKS AND CAICOS ISLANDS", "fmt", "%N%n%O%n%A%n%X%n%C%n%Z", @@ -1111,10 +1095,9 @@ class RegionDataConstants { "name", "THAILAND", "lang", "th", "languages", "th", - "fmt", "%N%n%O%n%A%n%C%n%S %Z", - "lfmt", "%N%n%O%n%A%n%C%n%S %Z", + "fmt", "%N%n%O%n%A%n%D %C%n%S %Z", + "lfmt", "%N%n%O%n%A%n%D, %C%n%S %Z", "upper", "S", - "fmtCharsets", "TIS-620", }), TJ(new String[]{ "name", "TAJIKISTAN", @@ -1161,7 +1144,6 @@ class RegionDataConstants { "lfmt", "%N%n%O%n%A%n%C, %S %Z", "require", "ACSZ", "state_name_type", "county", - "fmtCharsets", "Big5", }), TZ(new String[]{ "name", "TANZANIA (UNITED REP.)", @@ -1250,6 +1232,10 @@ class RegionDataConstants { WS(new String[]{ "name", "SAMOA", }), + XK(new String[]{ + "name", "KOSOVO", + "fmt", "%N%n%O%n%A%n%Z %C" + }), YE(new String[]{ "name", "YEMEN", "require", "AC", @@ -1283,7 +1269,6 @@ class RegionDataConstants { "upper", "C", "zip_name_type", "postal", "state_name_type", "province", - "dir", "ltr", }); private String jsonString; @@ -1306,4 +1291,20 @@ class RegionDataConstants { static Map<String, String> getCountryFormatMap() { return COUNTRY_FORMAT_MAP; } + + /** + * Assumes the array is a well-formed array - i.e., there are no unmatched keys in the input. + * Package-private so it can be accessed by tests. + */ + static String convertArrayToJsonString(String[] input) { + JSONObject object = new JSONObject(); + for (int i = 0; i < input.length; i += 2) { + try { + object.put(input[i], input[i + 1]); + } catch (JSONException e) { + // Ignore for now. + } + } + return object.toString(); + } } |