aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2014-07-20 18:26:40 -0700
committerBen Murdoch <benm@google.com>2014-07-20 18:26:40 -0700
commit7127f6844fac19d7610e34f4f7e03398fcd95531 (patch)
treeda5ffbd0f881753a61822e44262dc2b0e56ea7cf
parent6ce3a9ad00160cd58574b6bca6d2220c4dbfc83e (diff)
parent180296b5fc61c6eefb2e785cfdd95b9704f22881 (diff)
downloadsrc-7127f6844fac19d7610e34f4f7e03398fcd95531.tar.gz
Merge from Chromium at DEPS revision 284076
This commit was generated by merge_to_master.py. Change-Id: I9522edea682e4f4c26596b868632266c29a3f5c8
-rw-r--r--cpp/include/libaddressinput/address_formatter.h4
-rw-r--r--cpp/include/libaddressinput/address_input_helper.h2
-rw-r--r--cpp/include/libaddressinput/address_metadata.h2
-rw-r--r--cpp/include/libaddressinput/address_normalizer.h2
-rw-r--r--cpp/libaddressinput.gypi1
-rw-r--r--cpp/res/messages.grdp6
-rw-r--r--cpp/src/address_formatter.cc95
-rw-r--r--cpp/src/address_metadata.cc1
-rw-r--r--cpp/src/lookup_key.cc26
-rw-r--r--cpp/src/post_box_matchers.h2
-rw-r--r--cpp/src/preload_supplier.cc10
-rw-r--r--cpp/src/region_data_constants.cc14
-rw-r--r--cpp/src/rule.cc79
-rw-r--r--cpp/src/rule.h2
-rw-r--r--cpp/src/util/cctype_tolower_equal.cc44
-rw-r--r--cpp/src/util/cctype_tolower_equal.h35
-rw-r--r--cpp/src/util/json.cc22
-rw-r--r--cpp/src/util/json.h14
-rw-r--r--cpp/src/validating_util.cc5
-rw-r--r--cpp/test/address_input_helper_test.cc2
-rw-r--r--cpp/test/fake_downloader_test.cc7
-rw-r--r--cpp/test/fake_storage_test.cc7
-rw-r--r--cpp/test/null_storage_test.cc5
-rw-r--r--cpp/test/post_box_matchers_test.cc2
-rw-r--r--cpp/test/retriever_test.cc7
-rw-r--r--cpp/test/rule_retriever_test.cc7
-rw-r--r--cpp/test/util/json_test.cc42
-rw-r--r--cpp/test/validating_storage_test.cc7
-rw-r--r--java/res/values/address_strings.xml7
-rw-r--r--java/src/com/android/i18n/addressinput/AddressDataKey.java8
-rw-r--r--java/src/com/android/i18n/addressinput/AddressWidget.java25
-rw-r--r--java/src/com/android/i18n/addressinput/FormatInterpreter.java24
-rw-r--r--java/src/com/android/i18n/addressinput/RegionDataConstants.java15
-rw-r--r--java/test/com/android/i18n/addressinput/ClientDataTest.java2
-rw-r--r--java/test/com/android/i18n/addressinput/FormatInterpreterTest.java42
35 files changed, 365 insertions, 210 deletions
diff --git a/cpp/include/libaddressinput/address_formatter.h b/cpp/include/libaddressinput/address_formatter.h
index e267884..3ac94c3 100644
--- a/cpp/include/libaddressinput/address_formatter.h
+++ b/cpp/include/libaddressinput/address_formatter.h
@@ -45,6 +45,10 @@ void GetFormattedNationalAddressLine(
void GetStreetAddressLinesAsSingleLine(
const AddressData& address_data, std::string* line);
+// Returns the separator used for collapsing multiple parts of an address into a
+// single line.
+std::string GetLineSeparatorForLanguage(const std::string& language_tag);
+
} // namespace addressinput
} // namespace i18n
diff --git a/cpp/include/libaddressinput/address_input_helper.h b/cpp/include/libaddressinput/address_input_helper.h
index ce52cf9..043d3fc 100644
--- a/cpp/include/libaddressinput/address_input_helper.h
+++ b/cpp/include/libaddressinput/address_input_helper.h
@@ -23,9 +23,9 @@ namespace i18n {
namespace addressinput {
class LookupKey;
-class Node;
class PreloadSupplier;
struct AddressData;
+struct Node;
class AddressInputHelper {
public:
diff --git a/cpp/include/libaddressinput/address_metadata.h b/cpp/include/libaddressinput/address_metadata.h
index 269f982..cbe72db 100644
--- a/cpp/include/libaddressinput/address_metadata.h
+++ b/cpp/include/libaddressinput/address_metadata.h
@@ -17,6 +17,8 @@
#include <libaddressinput/address_field.h>
+#include <string>
+
namespace i18n {
namespace addressinput {
diff --git a/cpp/include/libaddressinput/address_normalizer.h b/cpp/include/libaddressinput/address_normalizer.h
index 6bd3366..06b7eb2 100644
--- a/cpp/include/libaddressinput/address_normalizer.h
+++ b/cpp/include/libaddressinput/address_normalizer.h
@@ -21,9 +21,9 @@
namespace i18n {
namespace addressinput {
-class AddressData;
class PreloadSupplier;
class StringCompare;
+struct AddressData;
class AddressNormalizer {
public:
diff --git a/cpp/libaddressinput.gypi b/cpp/libaddressinput.gypi
index 8d9030f..03ecaf9 100644
--- a/cpp/libaddressinput.gypi
+++ b/cpp/libaddressinput.gypi
@@ -40,6 +40,7 @@
'src/retriever.cc',
'src/rule.cc',
'src/rule_retriever.cc',
+ 'src/util/cctype_tolower_equal.cc',
'src/util/json.cc',
'src/util/md5.cc',
'src/util/string_compare.cc',
diff --git a/cpp/res/messages.grdp b/cpp/res/messages.grdp
index 1e1bcf2..c04449a 100644
--- a/cpp/res/messages.grdp
+++ b/cpp/res/messages.grdp
@@ -85,6 +85,12 @@ limitations under the License.
Island
</message>
<message
+ name="IDS_LIBADDRESSINPUT_OBLAST"
+ desc="Administrative Area for certain countries (e.g., Russia's
+ Leningrad).">
+ Oblast
+ </message>
+ <message
name="IDS_LIBADDRESSINPUT_PARISH"
desc="Administrative Area for certain countries (e.g., Andorra's
Canillo).">
diff --git a/cpp/src/address_formatter.cc b/cpp/src/address_formatter.cc
index 29f3486..da5bd07 100644
--- a/cpp/src/address_formatter.cc
+++ b/cpp/src/address_formatter.cc
@@ -14,20 +14,20 @@
#include <libaddressinput/address_formatter.h>
-#include <strings.h>
+#include <libaddressinput/address_data.h>
+#include <libaddressinput/address_field.h>
#include <algorithm>
#include <cstddef>
+#include <functional>
#include <string>
#include <vector>
-#include <libaddressinput/address_data.h>
-#include <libaddressinput/address_field.h>
-
#include "format_element.h"
#include "language.h"
#include "region_data_constants.h"
#include "rule.h"
+#include "util/cctype_tolower_equal.h"
namespace i18n {
namespace addressinput {
@@ -66,53 +66,6 @@ const char* kLanguagesThatUseAnArabicComma[] = {
"uz"
};
-// Case insensitive matcher for language tags.
-struct LanguageMatcher {
- LanguageMatcher(const std::string& tag) : tag(tag) {}
- std::string tag;
- bool operator() (const std::string& s) {
- return strcasecmp(tag.c_str(), s.c_str()) == 0;
- }
-};
-
-std::string GetLineSeparatorForLanguage(const std::string& language_tag) {
- Language address_language(language_tag);
-
- // First deal with explicit script tags.
- if (address_language.has_latin_script) {
- return kCommaSeparator;
- }
-
- // Now guess something appropriate based on the base language.
- const std::string& base_language = address_language.base;
- if (std::find_if(kLanguagesThatUseSpace,
- kLanguagesThatUseSpace + arraysize(kLanguagesThatUseSpace),
- LanguageMatcher(base_language)) !=
- kLanguagesThatUseSpace + arraysize(kLanguagesThatUseSpace)) {
- return kSpaceSeparator;
- } else if (std::find_if(kLanguagesThatHaveNoSeparator,
- kLanguagesThatHaveNoSeparator +
- arraysize(kLanguagesThatHaveNoSeparator),
- LanguageMatcher(base_language)) !=
- kLanguagesThatHaveNoSeparator +
- arraysize(kLanguagesThatHaveNoSeparator)) {
- return "";
- } else if (std::find_if(kLanguagesThatUseAnArabicComma,
- kLanguagesThatUseAnArabicComma +
- arraysize(kLanguagesThatUseAnArabicComma),
- LanguageMatcher(base_language)) !=
- kLanguagesThatUseAnArabicComma +
- arraysize(kLanguagesThatUseAnArabicComma)) {
- return kArabicCommaSeparator;
- }
- // Either the language is a latin-script language, or no language was
- // specified. In the latter case we still return ", " as the most common
- // separator in use. In countries that don't use this, e.g. Thailand,
- // addresses are often written in latin script where this would still be
- // appropriate, so this is a reasonable default in the absence of information.
- return kCommaSeparator;
-}
-
void CombineLinesForLanguage(
const std::vector<std::string>& lines, const std::string& language_tag,
std::string *line) {
@@ -193,5 +146,45 @@ void GetStreetAddressLinesAsSingleLine(
address_data.address_line, address_data.language_code, line);
}
+std::string GetLineSeparatorForLanguage(const std::string& language_tag) {
+ Language address_language(language_tag);
+
+ // First deal with explicit script tags.
+ if (address_language.has_latin_script) {
+ return kCommaSeparator;
+ }
+
+ // Now guess something appropriate based on the base language.
+ const std::string& base_language = address_language.base;
+ if (std::find_if(kLanguagesThatUseSpace,
+ kLanguagesThatUseSpace + arraysize(kLanguagesThatUseSpace),
+ std::bind2nd(EqualToTolowerString(), base_language)) !=
+ kLanguagesThatUseSpace + arraysize(kLanguagesThatUseSpace)) {
+ return kSpaceSeparator;
+ } else if (std::find_if(
+ kLanguagesThatHaveNoSeparator,
+ kLanguagesThatHaveNoSeparator +
+ arraysize(kLanguagesThatHaveNoSeparator),
+ std::bind2nd(EqualToTolowerString(), base_language)) !=
+ kLanguagesThatHaveNoSeparator +
+ arraysize(kLanguagesThatHaveNoSeparator)) {
+ return "";
+ } else if (std::find_if(
+ kLanguagesThatUseAnArabicComma,
+ kLanguagesThatUseAnArabicComma +
+ arraysize(kLanguagesThatUseAnArabicComma),
+ std::bind2nd(EqualToTolowerString(), base_language)) !=
+ kLanguagesThatUseAnArabicComma +
+ arraysize(kLanguagesThatUseAnArabicComma)) {
+ return kArabicCommaSeparator;
+ }
+ // Either the language is a latin-script language, or no language was
+ // specified. In the latter case we still return ", " as the most common
+ // separator in use. In countries that don't use this, e.g. Thailand,
+ // addresses are often written in latin script where this would still be
+ // appropriate, so this is a reasonable default in the absence of information.
+ return kCommaSeparator;
+}
+
} // namespace addressinput
} // namespace i18n
diff --git a/cpp/src/address_metadata.cc b/cpp/src/address_metadata.cc
index 59030ef..2310f2e 100644
--- a/cpp/src/address_metadata.cc
+++ b/cpp/src/address_metadata.cc
@@ -17,6 +17,7 @@
#include <libaddressinput/address_field.h>
#include <algorithm>
+#include <string>
#include "region_data_constants.h"
#include "rule.h"
diff --git a/cpp/src/lookup_key.cc b/cpp/src/lookup_key.cc
index b6f73fc..fbd9a50 100644
--- a/cpp/src/lookup_key.cc
+++ b/cpp/src/lookup_key.cc
@@ -14,20 +14,22 @@
#include "lookup_key.h"
+#include <libaddressinput/address_data.h>
+#include <libaddressinput/address_field.h>
+#include <libaddressinput/util/basictypes.h>
+
#include <cassert>
#include <cstddef>
+#include <functional>
#include <map>
#include <string>
#include <utility>
#include <vector>
-#include <libaddressinput/address_data.h>
-#include <libaddressinput/address_field.h>
-#include <libaddressinput/util/basictypes.h>
-
#include "language.h"
#include "region_data_constants.h"
#include "rule.h"
+#include "util/cctype_tolower_equal.h"
namespace i18n {
namespace addressinput {
@@ -39,15 +41,6 @@ const char kDashDelim[] = "--";
const char kData[] = "data";
const char kUnknown[] = "ZZ";
-// Case insensitive matcher for language tags.
-struct LanguageMatcher {
- LanguageMatcher(const std::string& tag) : tag(tag) { }
- std::string tag;
- bool operator() (const std::string& s) {
- return strcasecmp(tag.c_str(), s.c_str()) == 0;
- }
-};
-
// Assume the language_tag has had "Latn" script removed when this is called.
bool ShouldSetLanguageForKey(const std::string& language_tag,
const std::string& region_code) {
@@ -69,10 +62,9 @@ bool ShouldSetLanguageForKey(const std::string& language_tag,
return false;
}
// Finally, only return true if the language is one of the remaining ones.
- return std::find_if(languages.begin() + 1,
- languages.end(),
- LanguageMatcher(language_tag))
- != languages.end();
+ return std::find_if(languages.begin() + 1, languages.end(),
+ std::bind2nd(EqualToTolowerString(), language_tag)) !=
+ languages.end();
}
} // namespace
diff --git a/cpp/src/post_box_matchers.h b/cpp/src/post_box_matchers.h
index 8f9f44b..c269f11 100644
--- a/cpp/src/post_box_matchers.h
+++ b/cpp/src/post_box_matchers.h
@@ -22,8 +22,8 @@
namespace i18n {
namespace addressinput {
-class RE2ptr;
class Rule;
+struct RE2ptr;
class PostBoxMatchers {
public:
diff --git a/cpp/src/preload_supplier.cc b/cpp/src/preload_supplier.cc
index 31325bd..3f3dc0e 100644
--- a/cpp/src/preload_supplier.cc
+++ b/cpp/src/preload_supplier.cc
@@ -53,15 +53,11 @@ class IndexLess : public std::binary_function<std::string, std::string, bool> {
public:
result_type operator()(const first_argument_type& a,
const second_argument_type& b) const {
+ static const StringCompare kStringCompare;
return kStringCompare.NaturalLess(a, b);
}
-
- private:
- static const StringCompare kStringCompare;
};
-const StringCompare IndexLess::kStringCompare;
-
} // namespace
class IndexMap : public std::map<std::string, const Rule*, IndexLess> {};
@@ -105,6 +101,7 @@ class Helper {
(void)status; // Prevent unused variable if assert() is optimized away.
Json json;
+ std::string id;
std::vector<const Rule*> sub_rules;
if (!success) {
@@ -124,11 +121,10 @@ class Helper {
}
const Json& value = json.GetDictionaryValueForKey(*it);
- if (!value.HasStringValueForKey("id")) {
+ if (!value.GetStringValueForKey("id", &id)) {
success = false;
goto callback;
}
- const std::string& id = value.GetStringValueForKey("id");
assert(*it == id); // Sanity check.
size_t depth = std::count(id.begin(), id.end(), '/') - 1;
diff --git a/cpp/src/region_data_constants.cc b/cpp/src/region_data_constants.cc
index cabee28..0a1f78b 100644
--- a/cpp/src/region_data_constants.cc
+++ b/cpp/src/region_data_constants.cc
@@ -43,9 +43,8 @@ std::map<std::string, std::string> InitRegionData() {
"\"languages\":\"en\""
"}"));
region_data.insert(std::make_pair("AD", "{"
- "\"fmt\":\"%N%n%O%n%A%n%Z %S\","
- "\"require\":\"AS\","
- "\"state_name_type\":\"parish\","
+ "\"fmt\":\"%N%n%O%n%A%n%Z %C\","
+ "\"require\":\"A\","
"\"zipex\":\"AD100,AD501,AD700\","
"\"posturl\":\"http://www.correos.es/comun/CodigosPostales/1010_s-CodPostal.asp\?Provincia=\","
"\"languages\":\"ca\""
@@ -119,7 +118,7 @@ std::map<std::string, std::string> InitRegionData() {
"\"languages\":\"nl~pap\""
"}"));
region_data.insert(std::make_pair("AX", "{"
- "\"fmt\":\"%O%n%N%n%A%nAX-%Z %C%n\u00c5LAND\","
+ "\"fmt\":\"%O%n%N%n%A%nAX-%Z %C%n\\u00c5LAND\","
"\"require\":\"ACZ\","
"\"zipex\":\"22150,22550,22240,22710,22270,22730,22430\","
"\"posturl\":\"http://www.posten.ax/department.con\?iPage=123\","
@@ -329,7 +328,7 @@ std::map<std::string, std::string> InitRegionData() {
"\"languages\":\"ar~fr\""
"}"));
region_data.insert(std::make_pair("DK", "{"
- "\"fmt\":\"%O%n%N%n%A%n%Z %C\","
+ "\"fmt\":\"%N%n%O%n%A%n%Z %C\","
"\"require\":\"ACZ\","
"\"zipex\":\"8660,1566\","
"\"posturl\":\"http://www.postdanmark.dk/da/Privat/Kundeservice/postnummerkort/Sider/Find-postnummer.aspx\","
@@ -634,7 +633,7 @@ std::map<std::string, std::string> InitRegionData() {
"\"languages\":\"ar\""
"}"));
region_data.insert(std::make_pair("JP", "{"
- "\"fmt\":\"\u3012%Z%n%S%C%n%A%n%O%n%N\","
+ "\"fmt\":\"\\u3012%Z%n%S%C%n%A%n%O%n%N\","
"\"lfmt\":\"%N%n%O%n%A%n%C, %S%n%Z\","
"\"require\":\"ACSZ\","
"\"state_name_type\":\"prefecture\","
@@ -1062,8 +1061,9 @@ std::map<std::string, std::string> InitRegionData() {
"\"languages\":\"sr-Cyrl~sr-Latn\""
"}"));
region_data.insert(std::make_pair("RU", "{"
- "\"fmt\":\"%Z %C %n%A%n%O%n%N\","
+ "\"fmt\":\"%N%n%O%n%A%n%C%n%S%n%Z\","
"\"require\":\"ACZ\","
+ "\"state_name_type\":\"oblast\","
"\"zipex\":\"125075,247112,103375\","
"\"posturl\":\"http://info.russianpost.ru/servlet/department\","
"\"languages\":\"ru\""
diff --git a/cpp/src/rule.cc b/cpp/src/rule.cc
index e25142f..4d1cff5 100644
--- a/cpp/src/rule.cc
+++ b/cpp/src/rule.cc
@@ -38,20 +38,6 @@ namespace {
typedef std::map<std::string, int> NameMessageIdMap;
-const char kAdminAreaNameTypeKey[] = "state_name_type";
-const char kFormatKey[] = "fmt";
-const char kIdKey[] = "id";
-const char kLanguagesKey[] = "languages";
-const char kLatinFormatKey[] = "lfmt";
-const char kLatinNameKey[] = "lname";
-const char kNameKey[] = "name";
-const char kPostalCodeNameTypeKey[] = "zip_name_type";
-const char kRequireKey[] = "require";
-const char kSubKeysKey[] = "sub_keys";
-const char kZipKey[] = "zip";
-const char kPostalCodeExampleKey[] = "zipex";
-const char kPostServiceUrlKey[] = "posturl";
-
// Used as a separator in a list of items. For example, the list of supported
// languages can be "de~fr~it".
const char kSeparator = '~';
@@ -73,6 +59,8 @@ NameMessageIdMap InitAdminAreaMessageIds() {
message_ids.insert(std::make_pair(
"island", IDS_LIBADDRESSINPUT_ISLAND));
message_ids.insert(std::make_pair(
+ "oblast", IDS_LIBADDRESSINPUT_OBLAST));
+ message_ids.insert(std::make_pair(
"parish", IDS_LIBADDRESSINPUT_PARISH));
message_ids.insert(std::make_pair(
"prefecture", IDS_LIBADDRESSINPUT_PREFECTURE));
@@ -180,36 +168,33 @@ bool Rule::ParseSerializedRule(const std::string& serialized_rule) {
}
void Rule::ParseJsonRule(const Json& json) {
- if (json.HasStringValueForKey(kIdKey)) {
- id_ = json.GetStringValueForKey(kIdKey);
+ std::string value;
+ if (json.GetStringValueForKey("id", &value)) {
+ id_.swap(value);
}
- if (json.HasStringValueForKey(kFormatKey)) {
- ParseFormatRule(json.GetStringValueForKey(kFormatKey), &format_);
+ if (json.GetStringValueForKey("fmt", &value)) {
+ ParseFormatRule(value, &format_);
}
- if (json.HasStringValueForKey(kLatinFormatKey)) {
- ParseFormatRule(json.GetStringValueForKey(kLatinFormatKey), &latin_format_);
+ if (json.GetStringValueForKey("lfmt", &value)) {
+ ParseFormatRule(value, &latin_format_);
}
- if (json.HasStringValueForKey(kRequireKey)) {
- ParseAddressFieldsRequired(
- json.GetStringValueForKey(kRequireKey), &required_);
+ if (json.GetStringValueForKey("require", &value)) {
+ ParseAddressFieldsRequired(value, &required_);
}
- if (json.HasStringValueForKey(kSubKeysKey)) {
- SplitString(
- json.GetStringValueForKey(kSubKeysKey), kSeparator, &sub_keys_);
+ if (json.GetStringValueForKey("sub_keys", &value)) {
+ SplitString(value, kSeparator, &sub_keys_);
}
- if (json.HasStringValueForKey(kLanguagesKey)) {
- SplitString(
- json.GetStringValueForKey(kLanguagesKey), kSeparator, &languages_);
+ if (json.GetStringValueForKey("languages", &value)) {
+ SplitString(value, kSeparator, &languages_);
}
sole_postal_code_.clear();
- if (json.HasStringValueForKey(kZipKey)) {
- const std::string& zip = json.GetStringValueForKey(kZipKey);
+ if (json.GetStringValueForKey("zip", &value)) {
// The "zip" field in the JSON data is used in two different ways to
// validate the postal code. At the country level, the "zip" field indicates
// a Java compatible regular expression corresponding to all postal codes in
@@ -223,7 +208,7 @@ 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("^(" + value + ")", options);
if (matcher->ok()) {
postal_code_matcher_.reset(new RE2ptr(matcher));
} else {
@@ -232,37 +217,35 @@ void Rule::ParseJsonRule(const Json& json) {
}
// 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 (!ContainsRegExSpecialCharacters(value)) {
+ sole_postal_code_.swap(value);
}
}
- if (json.HasStringValueForKey(kAdminAreaNameTypeKey)) {
+ if (json.GetStringValueForKey("state_name_type", &value)) {
admin_area_name_message_id_ =
- GetMessageIdFromName(json.GetStringValueForKey(kAdminAreaNameTypeKey),
- GetAdminAreaMessageIds());
+ GetMessageIdFromName(value, GetAdminAreaMessageIds());
}
- if (json.HasStringValueForKey(kPostalCodeNameTypeKey)) {
+ if (json.GetStringValueForKey("zip_name_type", &value)) {
postal_code_name_message_id_ =
- GetMessageIdFromName(json.GetStringValueForKey(kPostalCodeNameTypeKey),
- GetPostalCodeMessageIds());
+ GetMessageIdFromName(value, GetPostalCodeMessageIds());
}
- if (json.HasStringValueForKey(kNameKey)) {
- name_ = json.GetStringValueForKey(kNameKey);
+ if (json.GetStringValueForKey("name", &value)) {
+ name_.swap(value);
}
- if (json.HasStringValueForKey(kLatinNameKey)) {
- latin_name_ = json.GetStringValueForKey(kLatinNameKey);
+ if (json.GetStringValueForKey("lname", &value)) {
+ latin_name_.swap(value);
}
- if (json.HasStringValueForKey(kPostalCodeExampleKey)) {
- postal_code_example_ = json.GetStringValueForKey(kPostalCodeExampleKey);
+ if (json.GetStringValueForKey("zipex", &value)) {
+ postal_code_example_.swap(value);
}
- if (json.HasStringValueForKey(kPostServiceUrlKey)) {
- post_service_url_ = json.GetStringValueForKey(kPostServiceUrlKey);
+ if (json.GetStringValueForKey("posturl", &value)) {
+ post_service_url_.swap(value);
}
}
diff --git a/cpp/src/rule.h b/cpp/src/rule.h
index c722932..38f20b3 100644
--- a/cpp/src/rule.h
+++ b/cpp/src/rule.h
@@ -33,7 +33,7 @@ namespace i18n {
namespace addressinput {
class Json;
-class RE2ptr;
+struct RE2ptr;
// Stores address metadata addressing rules, to be used for determining the
// layout of an address input widget or for address validation. Sample usage:
diff --git a/cpp/src/util/cctype_tolower_equal.cc b/cpp/src/util/cctype_tolower_equal.cc
new file mode 100644
index 0000000..819138f
--- /dev/null
+++ b/cpp/src/util/cctype_tolower_equal.cc
@@ -0,0 +1,44 @@
+// Copyright (C) 2014 Google Inc.
+//
+// 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.
+
+#include "cctype_tolower_equal.h"
+
+#include <algorithm>
+#include <cctype>
+#include <functional>
+#include <string>
+
+namespace i18n {
+namespace addressinput {
+
+namespace {
+
+struct EqualToTolowerChar
+ : public std::binary_function<std::string::value_type,
+ std::string::value_type, bool> {
+ result_type operator()(first_argument_type a, second_argument_type b) const {
+ return std::tolower(a) == std::tolower(b);
+ }
+};
+
+} // namespace
+
+EqualToTolowerString::result_type EqualToTolowerString::operator()(
+ const first_argument_type& a, const second_argument_type& b) const {
+ return a.size() == b.size() &&
+ std::equal(a.begin(), a.end(), b.begin(), EqualToTolowerChar());
+}
+
+} // addressinput
+} // i18n
diff --git a/cpp/src/util/cctype_tolower_equal.h b/cpp/src/util/cctype_tolower_equal.h
new file mode 100644
index 0000000..106087e
--- /dev/null
+++ b/cpp/src/util/cctype_tolower_equal.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2014 Google Inc.
+//
+// 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.
+
+#ifndef I18N_ADDRESSINPUT_UTIL_CCTYPE_TOLOWER_EQUAL_H_
+#define I18N_ADDRESSINPUT_UTIL_CCTYPE_TOLOWER_EQUAL_H_
+
+#include <functional>
+#include <string>
+
+namespace i18n {
+namespace addressinput {
+
+// Performs case insensitive comparison of |a| and |b| by calling std::tolower()
+// from <cctype>.
+struct EqualToTolowerString
+ : public std::binary_function<std::string, std::string, bool> {
+ result_type operator()(const first_argument_type& a,
+ const second_argument_type& b) const;
+};
+
+} // addressinput
+} // i18n
+
+#endif // I18N_ADDRESSINPUT_UTIL_CCTYPE_TOLOWER_EQUAL_H_
diff --git a/cpp/src/util/json.cc b/cpp/src/util/json.cc
index d819bb2..352c62b 100644
--- a/cpp/src/util/json.cc
+++ b/cpp/src/util/json.cc
@@ -139,23 +139,19 @@ const std::vector<std::string>& Json::GetKeys() const {
return impl_->GetKeys();
}
-bool Json::HasStringValueForKey(const std::string& key) const {
- assert(impl_ != NULL);
-
- // Member is owned by impl_.
- const Value::Member* member = impl_->FindMember(key);
- return member != NULL && member->value.IsString();
-}
-
-std::string Json::GetStringValueForKey(const std::string& key) const {
+bool Json::GetStringValueForKey(const std::string& key,
+ std::string* value) const {
assert(impl_ != NULL);
+ assert(value != NULL);
// Member is owned by impl_.
const Value::Member* member = impl_->FindMember(key.c_str());
- assert(member != NULL);
- assert(member->value.IsString());
- return std::string(member->value.GetString(),
- member->value.GetStringLength());
+ if (member == NULL || !member->value.IsString()) {
+ return false;
+ }
+
+ value->assign(member->value.GetString(), member->value.GetStringLength());
+ return true;
}
bool Json::HasDictionaryValueForKey(const std::string& key) const {
diff --git a/cpp/src/util/json.h b/cpp/src/util/json.h
index 1fbf500..90565e6 100644
--- a/cpp/src/util/json.h
+++ b/cpp/src/util/json.h
@@ -43,15 +43,11 @@ class Json {
// successfully in ParseObject() before invoking this method.
const std::vector<std::string>& GetKeys() const;
- // Returns true if the parsed JSON contains a string value for |key|. The JSON
- // object must be parsed successfully in ParseObject() before invoking this
- // method.
- bool HasStringValueForKey(const std::string& key) const;
-
- // Returns the string value for the |key|. The |key| must be present and its
- // value must be of string type, i.e., HasStringValueForKey(key) must return
- // true before invoking this method.
- std::string GetStringValueForKey(const std::string& key) const;
+ // Returns true if the parsed JSON contains a string value for |key|. Sets
+ // |value| to the string value of the |key|. The JSON object must be parsed
+ // successfully in ParseObject() before invoking this method. The |value|
+ // parameter should not be NULL.
+ bool GetStringValueForKey(const std::string& key, std::string* value) const;
// Returns true if the parsed JSON contains a dictionary value for |key|. The
// JSON object must be parsed successfully in ParseObject() before invoking
diff --git a/cpp/src/validating_util.cc b/cpp/src/validating_util.cc
index 6b52e8b..df3fc06 100644
--- a/cpp/src/validating_util.cc
+++ b/cpp/src/validating_util.cc
@@ -84,7 +84,10 @@ bool UnwrapHeader(const char* header_prefix,
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);
+ int size = std::sprintf(timestamp_string, "%ld", timestamp);
+ assert(size > 0);
+ assert(size < sizeof timestamp_string);
+ (void)size;
std::string header;
header.append(kTimestampPrefix, kTimestampPrefixLength);
diff --git a/cpp/test/address_input_helper_test.cc b/cpp/test/address_input_helper_test.cc
index 84a2286..bb683da 100644
--- a/cpp/test/address_input_helper_test.cc
+++ b/cpp/test/address_input_helper_test.cc
@@ -326,7 +326,7 @@ TEST_F(AddressInputHelperMockDataTest,
"{\"data/KR\": "
// The top-level ZIP expression must be present for sub-key matches to be
// evaluated.
- "{\"id\":\"data/KR\", \"sub_keys\":\"A~B\", \"zip\":\"\\\\d\{5}\"}, "
+ "{\"id\":\"data/KR\", \"sub_keys\":\"A~B\", \"zip\":\"\\\\d{5}\"}, "
"\"data/KR/A\": "
"{\"id\":\"data/KR/A\", \"sub_keys\":\"A1~A2\"}, "
"\"data/KR/A/A1\": "
diff --git a/cpp/test/fake_downloader_test.cc b/cpp/test/fake_downloader_test.cc
index 2d88acc..6468277 100644
--- a/cpp/test/fake_downloader_test.cc
+++ b/cpp/test/fake_downloader_test.cc
@@ -16,6 +16,7 @@
#include <libaddressinput/callback.h>
#include <libaddressinput/downloader.h>
+#include <libaddressinput/util/basictypes.h>
#include <libaddressinput/util/scoped_ptr.h>
#include <cstddef>
@@ -27,7 +28,6 @@
namespace {
-using i18n::addressinput::BuildCallback;
using i18n::addressinput::Downloader;
using i18n::addressinput::FakeDownloader;
using i18n::addressinput::RegionDataConstants;
@@ -40,7 +40,8 @@ class FakeDownloaderTest : public testing::TestWithParam<std::string> {
virtual ~FakeDownloaderTest() {}
Downloader::Callback* BuildCallback() {
- return ::BuildCallback(this, &FakeDownloaderTest::OnDownloaded);
+ return i18n::addressinput::BuildCallback(
+ this, &FakeDownloaderTest::OnDownloaded);
}
FakeDownloader downloader_;
@@ -58,6 +59,8 @@ class FakeDownloaderTest : public testing::TestWithParam<std::string> {
delete data;
}
}
+
+ DISALLOW_COPY_AND_ASSIGN(FakeDownloaderTest);
};
// Returns testing::AssertionSuccess if |data| is valid downloaded data for
diff --git a/cpp/test/fake_storage_test.cc b/cpp/test/fake_storage_test.cc
index 49663ca..f950ee4 100644
--- a/cpp/test/fake_storage_test.cc
+++ b/cpp/test/fake_storage_test.cc
@@ -16,6 +16,7 @@
#include <libaddressinput/callback.h>
#include <libaddressinput/storage.h>
+#include <libaddressinput/util/basictypes.h>
#include <libaddressinput/util/scoped_ptr.h>
#include <cstddef>
@@ -25,7 +26,6 @@
namespace {
-using i18n::addressinput::BuildCallback;
using i18n::addressinput::FakeStorage;
using i18n::addressinput::scoped_ptr;
using i18n::addressinput::Storage;
@@ -37,7 +37,8 @@ class FakeStorageTest : public testing::Test {
virtual ~FakeStorageTest() {}
Storage::Callback* BuildCallback() {
- return ::BuildCallback(this, &FakeStorageTest::OnDataReady);
+ return i18n::addressinput::BuildCallback(
+ this, &FakeStorageTest::OnDataReady);
}
FakeStorage storage_;
@@ -57,6 +58,8 @@ class FakeStorageTest : public testing::Test {
delete data;
}
}
+
+ DISALLOW_COPY_AND_ASSIGN(FakeStorageTest);
};
TEST_F(FakeStorageTest, GetWithoutPutReturnsEmptyData) {
diff --git a/cpp/test/null_storage_test.cc b/cpp/test/null_storage_test.cc
index dca0803..e586568 100644
--- a/cpp/test/null_storage_test.cc
+++ b/cpp/test/null_storage_test.cc
@@ -25,7 +25,6 @@
namespace {
-using i18n::addressinput::BuildCallback;
using i18n::addressinput::NullStorage;
using i18n::addressinput::scoped_ptr;
using i18n::addressinput::Storage;
@@ -33,9 +32,11 @@ using i18n::addressinput::Storage;
class NullStorageTest : public testing::Test {
protected:
NullStorageTest() {}
+ virtual ~NullStorageTest() {}
Storage::Callback* BuildCallback() {
- return ::BuildCallback(this, &NullStorageTest::OnDataReady);
+ return i18n::addressinput::BuildCallback(
+ this, &NullStorageTest::OnDataReady);
}
NullStorage storage_;
diff --git a/cpp/test/post_box_matchers_test.cc b/cpp/test/post_box_matchers_test.cc
index d89ee0b..eb9230c 100644
--- a/cpp/test/post_box_matchers_test.cc
+++ b/cpp/test/post_box_matchers_test.cc
@@ -23,7 +23,7 @@
namespace i18n {
namespace addressinput {
-class RE2ptr;
+struct RE2ptr;
} // namespace addressinput
} // namespace i18n
diff --git a/cpp/test/retriever_test.cc b/cpp/test/retriever_test.cc
index aff418c..513e799 100644
--- a/cpp/test/retriever_test.cc
+++ b/cpp/test/retriever_test.cc
@@ -17,6 +17,7 @@
#include <libaddressinput/callback.h>
#include <libaddressinput/null_storage.h>
#include <libaddressinput/storage.h>
+#include <libaddressinput/util/basictypes.h>
#include <libaddressinput/util/scoped_ptr.h>
#include <cstddef>
@@ -33,7 +34,6 @@
namespace {
-using i18n::addressinput::BuildCallback;
using i18n::addressinput::FakeDownloader;
using i18n::addressinput::MockDownloader;
using i18n::addressinput::NullStorage;
@@ -68,7 +68,8 @@ class RetrieverTest : public testing::Test {
virtual ~RetrieverTest() {}
Retriever::Callback* BuildCallback() {
- return ::BuildCallback(this, &RetrieverTest::OnDataReady);
+ return i18n::addressinput::BuildCallback(
+ this, &RetrieverTest::OnDataReady);
}
Retriever retriever_;
@@ -84,6 +85,8 @@ class RetrieverTest : public testing::Test {
key_ = key;
data_ = data;
}
+
+ DISALLOW_COPY_AND_ASSIGN(RetrieverTest);
};
TEST_F(RetrieverTest, RetrieveData) {
diff --git a/cpp/test/rule_retriever_test.cc b/cpp/test/rule_retriever_test.cc
index ecfb0c8..9a32df3 100644
--- a/cpp/test/rule_retriever_test.cc
+++ b/cpp/test/rule_retriever_test.cc
@@ -16,6 +16,7 @@
#include <libaddressinput/callback.h>
#include <libaddressinput/null_storage.h>
+#include <libaddressinput/util/basictypes.h>
#include <libaddressinput/util/scoped_ptr.h>
#include <string>
@@ -28,7 +29,6 @@
namespace {
-using i18n::addressinput::BuildCallback;
using i18n::addressinput::FakeDownloader;
using i18n::addressinput::NullStorage;
using i18n::addressinput::Retriever;
@@ -50,7 +50,8 @@ class RuleRetrieverTest : public testing::Test {
virtual ~RuleRetrieverTest() {}
RuleRetriever::Callback* BuildCallback() {
- return ::BuildCallback(this, &RuleRetrieverTest::OnRuleReady);
+ return i18n::addressinput::BuildCallback(
+ this, &RuleRetrieverTest::OnRuleReady);
}
RuleRetriever rule_retriever_;
@@ -66,6 +67,8 @@ class RuleRetrieverTest : public testing::Test {
key_ = key;
rule_.CopyFrom(rule);
}
+
+ DISALLOW_COPY_AND_ASSIGN(RuleRetrieverTest);
};
TEST_F(RuleRetrieverTest, ExistingRule) {
diff --git a/cpp/test/util/json_test.cc b/cpp/test/util/json_test.cc
index f6eb389..a3b9f80 100644
--- a/cpp/test/util/json_test.cc
+++ b/cpp/test/util/json_test.cc
@@ -32,8 +32,9 @@ TEST(JsonTest, EmptyStringIsNotValid) {
TEST(JsonTest, EmptyDictionaryContainsNoKeys) {
Json json;
ASSERT_TRUE(json.ParseObject("{}"));
- EXPECT_FALSE(json.HasStringValueForKey("key"));
- EXPECT_FALSE(json.HasStringValueForKey(std::string()));
+ std::string not_checked;
+ EXPECT_FALSE(json.GetStringValueForKey("key", &not_checked));
+ EXPECT_FALSE(json.GetStringValueForKey(std::string(), &not_checked));
}
TEST(JsonTest, InvalidJsonIsNotValid) {
@@ -44,35 +45,40 @@ TEST(JsonTest, InvalidJsonIsNotValid) {
TEST(JsonTest, OneKeyIsValid) {
Json json;
ASSERT_TRUE(json.ParseObject("{\"key\": \"value\"}"));
- ASSERT_TRUE(json.HasStringValueForKey("key"));
- EXPECT_EQ("value", json.GetStringValueForKey("key"));
+ std::string value;
+ EXPECT_TRUE(json.GetStringValueForKey("key", &value));
+ EXPECT_EQ("value", value);
}
TEST(JsonTest, EmptyStringKeyIsNotInObject) {
Json json;
ASSERT_TRUE(json.ParseObject("{\"key\": \"value\"}"));
- EXPECT_FALSE(json.HasStringValueForKey(std::string()));
+ std::string not_checked;
+ EXPECT_FALSE(json.GetStringValueForKey(std::string(), &not_checked));
}
TEST(JsonTest, EmptyKeyIsValid) {
Json json;
ASSERT_TRUE(json.ParseObject("{\"\": \"value\"}"));
- ASSERT_TRUE(json.HasStringValueForKey(std::string()));
- EXPECT_EQ("value", json.GetStringValueForKey(std::string()));
+ std::string value;
+ EXPECT_TRUE(json.GetStringValueForKey(std::string(), &value));
+ EXPECT_EQ("value", value);
}
TEST(JsonTest, EmptyValueIsValid) {
Json json;
ASSERT_TRUE(json.ParseObject("{\"key\": \"\"}"));
- ASSERT_TRUE(json.HasStringValueForKey("key"));
- EXPECT_TRUE(json.GetStringValueForKey("key").empty());
+ std::string value;
+ EXPECT_TRUE(json.GetStringValueForKey("key", &value));
+ EXPECT_TRUE(value.empty());
}
TEST(JsonTest, Utf8EncodingIsValid) {
Json json;
ASSERT_TRUE(json.ParseObject("{\"key\": \"Ü\"}"));
- ASSERT_TRUE(json.HasStringValueForKey("key"));
- EXPECT_EQ("Ü", json.GetStringValueForKey("key"));
+ std::string value;
+ EXPECT_TRUE(json.GetStringValueForKey("key", &value));
+ EXPECT_EQ("Ü", value);
}
TEST(JsonTest, InvalidUtf8IsNotValid) {
@@ -89,11 +95,12 @@ TEST(JsonTest, NullInMiddleIsNotValid) {
TEST(JsonTest, TwoKeysAreValid) {
Json json;
ASSERT_TRUE(json.ParseObject("{\"key1\": \"value1\", \"key2\": \"value2\"}"));
- ASSERT_TRUE(json.HasStringValueForKey("key1"));
- EXPECT_EQ("value1", json.GetStringValueForKey("key1"));
+ std::string value;
+ EXPECT_TRUE(json.GetStringValueForKey("key1", &value));
+ EXPECT_EQ("value1", value);
- ASSERT_TRUE(json.HasStringValueForKey("key2"));
- EXPECT_EQ("value2", json.GetStringValueForKey("key2"));
+ EXPECT_TRUE(json.GetStringValueForKey("key2", &value));
+ EXPECT_EQ("value2", value);
}
TEST(JsonTest, ListIsNotValid) {
@@ -122,8 +129,9 @@ TEST(JsonTest, DictionaryFound) {
ASSERT_TRUE(json.ParseObject("{\"key\":{\"inner_key\":\"value\"}}"));
ASSERT_TRUE(json.HasDictionaryValueForKey("key"));
const Json& sub_json = json.GetDictionaryValueForKey("key");
- ASSERT_TRUE(sub_json.HasStringValueForKey("inner_key"));
- EXPECT_EQ("value", sub_json.GetStringValueForKey("inner_key"));
+ std::string value;
+ EXPECT_TRUE(sub_json.GetStringValueForKey("inner_key", &value));
+ EXPECT_EQ("value", value);
}
TEST(JsonTest, DictionariesHaveKeys) {
diff --git a/cpp/test/validating_storage_test.cc b/cpp/test/validating_storage_test.cc
index ced22dc..f786117 100644
--- a/cpp/test/validating_storage_test.cc
+++ b/cpp/test/validating_storage_test.cc
@@ -16,6 +16,7 @@
#include <libaddressinput/callback.h>
#include <libaddressinput/storage.h>
+#include <libaddressinput/util/basictypes.h>
#include <libaddressinput/util/scoped_ptr.h>
#include <cstddef>
@@ -31,7 +32,6 @@
namespace {
-using i18n::addressinput::BuildCallback;
using i18n::addressinput::FakeStorage;
using i18n::addressinput::scoped_ptr;
using i18n::addressinput::Storage;
@@ -57,7 +57,8 @@ class ValidatingStorageTest : public testing::Test {
virtual ~ValidatingStorageTest() {}
ValidatingStorage::Callback* BuildCallback() {
- return ::BuildCallback(this, &ValidatingStorageTest::OnDataReady);
+ return i18n::addressinput::BuildCallback(
+ this, &ValidatingStorageTest::OnDataReady);
}
Storage* const wrapped_storage_; // Owned by |storage_|.
@@ -76,6 +77,8 @@ class ValidatingStorageTest : public testing::Test {
delete data;
}
}
+
+ DISALLOW_COPY_AND_ASSIGN(ValidatingStorageTest);
};
TEST_F(ValidatingStorageTest, GoodData) {
diff --git a/java/res/values/address_strings.xml b/java/res/values/address_strings.xml
index 57b34c4..f12247a 100644
--- a/java/res/values/address_strings.xml
+++ b/java/res/values/address_strings.xml
@@ -80,6 +80,9 @@
<!-- Administrative Area for certain countries (e.g., Bahama's Cat Island) [CHAR LIMIT=30] -->
<string name="i18n_island">Island</string>
+ <!-- Administrative Area for certain countries (e.g., Russia's Leningrad) [CHAR LIMIT=30] -->
+ <string name="i18n_oblast">Oblast</string>
+
<!-- Administrative Area for certain countries (e.g., Andorra's Canillo) [CHAR LIMIT=30] -->
<string name="i18n_parish">Parish</string>
@@ -140,6 +143,10 @@
(e.g., Bahama) is entered incorrectly by the user [CHAR LIMIT=30] -->
<string name="invalid_island">Invalid island</string>
+ <!-- Message to be shown when the administrative area (oblast) for certain countries
+ (e.g., Russia) is entered incorrectly by the user [CHAR LIMIT=30] -->
+ <string name="invalid_oblast">Invalid oblast</string>
+
<!-- Message to be shown when the administrative area (parish) for certain countries
(e.g., Andorra) is entered incorrectly by the user [CHAR LIMIT=30] -->
<string name="invalid_parish">Invalid parish</string>
diff --git a/java/src/com/android/i18n/addressinput/AddressDataKey.java b/java/src/com/android/i18n/addressinput/AddressDataKey.java
index 33ffd74..a4766af 100644
--- a/java/src/com/android/i18n/addressinput/AddressDataKey.java
+++ b/java/src/com/android/i18n/addressinput/AddressDataKey.java
@@ -56,11 +56,15 @@ enum AddressDataKey {
*/
LFMT,
/**
+ * Indicates the type of the name used for the locality (city) field.
+ */
+ LOCALITY_NAME_TYPE,
+ /**
* Indicates which fields must be present in a valid address.
*/
REQUIRE,
/**
- * Indicates the name used for the admin areas for a particular region.
+ * Indicates the type of the name used for the state (administrative area) field.
*/
STATE_NAME_TYPE,
/**
@@ -90,7 +94,7 @@ enum AddressDataKey {
*/
ZIP,
/**
- * Indicates the name used for the postal code for a particular region.
+ * Indicates the type of the name used for the ZIP (postal code) field.
*/
ZIP_NAME_TYPE;
diff --git a/java/src/com/android/i18n/addressinput/AddressWidget.java b/java/src/com/android/i18n/addressinput/AddressWidget.java
index 2ebfeef..c377e2c 100644
--- a/java/src/com/android/i18n/addressinput/AddressWidget.java
+++ b/java/src/com/android/i18n/addressinput/AddressWidget.java
@@ -81,11 +81,16 @@ public class AddressWidget implements AdapterView.OnItemSelectedListener {
private ScriptType mScript;
+ // The appropriate label that should be applied to the locality (city) field of the current
+ // country. Examples include "city" or "district".
+ private String mLocalityLabel;
+
// The appropriate label that should be applied to the admin area field of the current country.
// Examples include "state", "province", "emirate", etc.
private String mAdminLabel;
private static final Map<String, Integer> ADMIN_LABELS;
+ private static final Map<String, Integer> LOCALITY_LABELS;
private static final Map<String, Integer> ADMIN_ERROR_MESSAGES;
private static final FormOptions SHOW_ALL_FIELDS = new FormOptions.Builder().build();
@@ -107,12 +112,18 @@ public class AddressWidget implements AdapterView.OnItemSelectedListener {
adminLabelMap.put("do_si", R.string.i18n_do_si);
adminLabelMap.put("emirate", R.string.i18n_emirate);
adminLabelMap.put("island", R.string.i18n_island);
+ adminLabelMap.put("oblast", R.string.i18n_oblast);
adminLabelMap.put("parish", R.string.i18n_parish);
adminLabelMap.put("prefecture", R.string.i18n_prefecture);
adminLabelMap.put("province", R.string.i18n_province);
adminLabelMap.put("state", R.string.i18n_state_label);
ADMIN_LABELS = Collections.unmodifiableMap(adminLabelMap);
+ Map<String, Integer> localityLabelMap = new HashMap<String, Integer>(2);
+ localityLabelMap.put("city", R.string.i18n_locality_label);
+ localityLabelMap.put("district", R.string.i18n_dependent_locality_label);
+ LOCALITY_LABELS = Collections.unmodifiableMap(localityLabelMap);
+
Map<String, Integer> adminErrorMap = new HashMap<String, Integer>(15);
adminErrorMap.put("area", R.string.invalid_area);
adminErrorMap.put("county", R.string.invalid_county_label);
@@ -121,6 +132,7 @@ public class AddressWidget implements AdapterView.OnItemSelectedListener {
adminErrorMap.put("do_si", R.string.invalid_do_si);
adminErrorMap.put("emirate", R.string.invalid_emirate);
adminErrorMap.put("island", R.string.invalid_island);
+ adminErrorMap.put("oblast", R.string.invalid_oblast);
adminErrorMap.put("parish", R.string.invalid_parish);
adminErrorMap.put("prefecture", R.string.invalid_prefecture);
adminErrorMap.put("province", R.string.invalid_province);
@@ -266,7 +278,7 @@ public class AddressWidget implements AdapterView.OnItemSelectedListener {
// Set up AddressField.LOCALITY
AddressUiComponent localityUi = new AddressUiComponent(AddressField.LOCALITY);
- localityUi.setFieldName(mContext.getString(R.string.i18n_locality_label));
+ localityUi.setFieldName(getLocalityFieldName(countryNode));
mInputWidgets.put(AddressField.LOCALITY, localityUi);
// Set up AddressField.DEPENDENT_LOCALITY
@@ -330,6 +342,17 @@ public class AddressWidget implements AdapterView.OnItemSelectedListener {
return zipName;
}
+ private String getLocalityFieldName(AddressVerificationNodeData countryNode) {
+ String localityLabelType = countryNode.get(AddressDataKey.LOCALITY_NAME_TYPE);
+ mLocalityLabel = localityLabelType;
+ Integer result = LOCALITY_LABELS.get(localityLabelType);
+ if (result == null) {
+ // Fallback to city.
+ result = R.string.i18n_locality_label;
+ }
+ return mContext.getString(result);
+ }
+
private String getAdminAreaFieldName(AddressVerificationNodeData countryNode) {
String adminLabelType = countryNode.get(AddressDataKey.STATE_NAME_TYPE);
mAdminLabel = adminLabelType;
diff --git a/java/src/com/android/i18n/addressinput/FormatInterpreter.java b/java/src/com/android/i18n/addressinput/FormatInterpreter.java
index ffd19b1..ed7b2ac 100644
--- a/java/src/com/android/i18n/addressinput/FormatInterpreter.java
+++ b/java/src/com/android/i18n/addressinput/FormatInterpreter.java
@@ -155,15 +155,16 @@ class FormatInterpreter {
List<String> lines = new ArrayList<String>();
StringBuilder currentLine = new StringBuilder();
- for (String substr : getFormatSubStrings(scriptType, regionCode)) {
- if (substr.equals(NEW_LINE)) {
- String normalizedStr = removeAllRedundantSpaces(currentLine.toString());
+ for (String formatSymbol : getFormatSubStrings(scriptType, regionCode)) {
+ if (formatSymbol.equals(NEW_LINE)) {
+ String normalizedStr =
+ removeRedundantSpacesAndLeadingPunctuation(currentLine.toString());
if (normalizedStr.length() > 0) {
lines.add(normalizedStr);
currentLine.setLength(0);
}
- } else if (substr.startsWith("%")) {
- char c = substr.charAt(1);
+ } else if (formatSymbol.startsWith("%")) {
+ char c = formatSymbol.charAt(1);
AddressField field = AddressField.of(c);
Util.checkNotNull(field, "null address field for character " + c);
@@ -203,10 +204,10 @@ class FormatInterpreter {
currentLine.append(value);
}
} else {
- currentLine.append(substr);
+ currentLine.append(formatSymbol);
}
}
- String normalizedStr = removeAllRedundantSpaces(currentLine.toString());
+ String normalizedStr = removeRedundantSpacesAndLeadingPunctuation(currentLine.toString());
if (normalizedStr.length() > 0) {
lines.add(normalizedStr);
}
@@ -242,13 +243,16 @@ class FormatInterpreter {
return parts;
}
- private String removeAllRedundantSpaces(String str) {
+ private static String removeRedundantSpacesAndLeadingPunctuation(String str) {
+ // Remove leading commas and other punctuation that might have been added by the formatter
+ // in the case of missing data.
+ str = str.replaceFirst("^[-,\\s]+", "");
str = str.trim();
str = str.replaceAll(" +", " ");
return str;
}
- private String getFormatString(ScriptType scriptType, String regionCode) {
+ private static String getFormatString(ScriptType scriptType, String regionCode) {
String format = (scriptType == ScriptType.LOCAL)
? getJsonValue(regionCode, AddressDataKey.FMT)
: getJsonValue(regionCode, AddressDataKey.LFMT);
@@ -258,7 +262,7 @@ class FormatInterpreter {
return format;
}
- private String getJsonValue(String regionCode, AddressDataKey key) {
+ private static String getJsonValue(String regionCode, AddressDataKey key) {
Util.checkNotNull(regionCode);
String jsonString = RegionDataConstants.getCountryFormatMap().get(regionCode);
Util.checkNotNull(jsonString, "no json data for region code " + regionCode);
diff --git a/java/src/com/android/i18n/addressinput/RegionDataConstants.java b/java/src/com/android/i18n/addressinput/RegionDataConstants.java
index 48affab..4f4c5b7 100644
--- a/java/src/com/android/i18n/addressinput/RegionDataConstants.java
+++ b/java/src/com/android/i18n/addressinput/RegionDataConstants.java
@@ -41,10 +41,8 @@ class RegionDataConstants {
"name", "ANDORRA",
"lang", "ca",
"languages", "ca",
- "fmt", "%N%n%O%n%A%n%Z %S",
- "require", "AS",
- "upper", "S",
- "state_name_type", "parish",
+ "fmt", "%N%n%O%n%A%n%Z %C",
+ "require", "A",
}),
AE(new String[]{
"name", "UNITED ARAB EMIRATES",
@@ -311,7 +309,7 @@ class RegionDataConstants {
}),
DK(new String[]{
"name", "DENMARK",
- "fmt", "%O%n%N%n%A%n%Z %C",
+ "fmt", "%N%n%O%n%A%n%Z %C",
"require", "ACZ",
}),
DM(new String[]{
@@ -977,8 +975,9 @@ class RegionDataConstants {
}),
RU(new String[]{
"name", "RUSSIAN FEDERATION",
- "fmt", "%Z %C %n%A%n%O%n%N",
+ "fmt", "%N%n%O%n%A%n%C%n%S%n%Z",
"require", "ACZ",
+ "state_name_type", "oblast",
"upper", "AC",
}),
RW(new String[]{
@@ -1124,6 +1123,7 @@ class RegionDataConstants {
"name", "TURKEY",
"fmt", "%N%n%O%n%A%n%Z %C/%S",
"require", "ACZ",
+ "locality_name_type", "district",
}),
TT(new String[]{
"name", "TRINIDAD AND TOBAGO",
@@ -1167,7 +1167,7 @@ class RegionDataConstants {
"name", "UNITED STATES",
"lang", "en",
"languages", "en",
- "fmt", "%N%n%O%n%A%n%C %S %Z",
+ "fmt", "%N%n%O%n%A%n%C, %S %Z",
"require", "ACSZ",
"upper", "CS",
"zip_name_type", "zip",
@@ -1269,6 +1269,7 @@ class RegionDataConstants {
"upper", "C",
"zip_name_type", "postal",
"state_name_type", "province",
+ "locality_name_type", "city",
});
private String jsonString;
diff --git a/java/test/com/android/i18n/addressinput/ClientDataTest.java b/java/test/com/android/i18n/addressinput/ClientDataTest.java
index 570434e..b18d726 100644
--- a/java/test/com/android/i18n/addressinput/ClientDataTest.java
+++ b/java/test/com/android/i18n/addressinput/ClientDataTest.java
@@ -78,6 +78,6 @@ public class ClientDataTest extends AsyncTestCase {
// data from there.
assertNotNull(data);
String unitedStatesFormatInfo = data.get(AddressDataKey.FMT);
- assertEquals("%N%n%O%n%A%n%C %S %Z", unitedStatesFormatInfo);
+ assertEquals("%N%n%O%n%A%n%C, %S %Z", unitedStatesFormatInfo);
}
}
diff --git a/java/test/com/android/i18n/addressinput/FormatInterpreterTest.java b/java/test/com/android/i18n/addressinput/FormatInterpreterTest.java
index 50372ed..f5bac79 100644
--- a/java/test/com/android/i18n/addressinput/FormatInterpreterTest.java
+++ b/java/test/com/android/i18n/addressinput/FormatInterpreterTest.java
@@ -147,7 +147,7 @@ public class FormatInterpreterTest extends TestCase {
public void testUsEnvelopeAddress() {
List<String> expected = new ArrayList<String>();
expected.add("1098 Alta Ave");
- expected.add("Mt View CA 94043");
+ expected.add("Mt View, CA 94043");
List<String> real = formatInterpreter.getEnvelopeAddress(US_CA_ADDRESS);
@@ -189,4 +189,44 @@ public class FormatInterpreterTest extends TestCase {
List<String> real = formatInterpreter.getEnvelopeAddress(address);
assertEquals(expected, real);
}
+
+ public void testEnvelopeAddressLeadingPostPrefix() {
+ List<String> expected = new ArrayList<String>();
+ expected.add("CH-8047 Herrliberg");
+ AddressData address = new AddressData.Builder().setCountry("CH")
+ .setPostalCode("8047")
+ .setLocality("Herrliberg")
+ .build();
+
+ List<String> real = formatInterpreter.getEnvelopeAddress(address);
+ assertEquals(expected, real);
+ }
+
+ public void testSvAddress() {
+ final AddressData svAddress = new AddressData.Builder().setCountry("SV")
+ .setAdminArea("Ahuachapán")
+ .setLocality("Ahuachapán")
+ .setAddressLine1("Some Street 12")
+ .build();
+
+ List<String> expected = new ArrayList<String>();
+ expected.add("Some Street 12");
+ expected.add("Ahuachapán");
+ expected.add("Ahuachapán");
+
+ List<String> real = formatInterpreter.getEnvelopeAddress(svAddress);
+ assertEquals(expected, real);
+
+ final AddressData svAddressWithPostCode = new AddressData.Builder(svAddress)
+ .setPostalCode("CP 2101")
+ .build();
+
+ expected = new ArrayList<String>();
+ expected.add("Some Street 12");
+ expected.add("CP 2101-Ahuachapán");
+ expected.add("Ahuachapán");
+
+ real = formatInterpreter.getEnvelopeAddress(svAddressWithPostCode);
+ assertEquals(expected, real);
+ }
}