1 // Copyright (C) 2013 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "rule.h" 16 17 #include <cassert> 18 #include <cstddef> 19 #include <map> 20 #include <string> 21 #include <utility> 22 23 #include <re2/re2.h> 24 25 #include "address_field_util.h" 26 #include "format_element.h" 27 #include "grit.h" 28 #include "messages.h" 29 #include "region_data_constants.h" 30 #include "util/json.h" 31 #include "util/re2ptr.h" 32 #include "util/string_split.h" 33 34 namespace i18n { 35 namespace addressinput { 36 37 namespace { 38 39 typedef std::map<std::string, int> NameMessageIdMap; 40 41 // Used as a separator in a list of items. For example, the list of supported 42 // languages can be "de~fr~it". 43 const char kSeparator = '~'; 44 45 NameMessageIdMap InitAdminAreaMessageIds() { 46 NameMessageIdMap message_ids; 47 message_ids.insert(std::make_pair( 48 "area", IDS_LIBADDRESSINPUT_AREA)); 49 message_ids.insert(std::make_pair( 50 "county", IDS_LIBADDRESSINPUT_COUNTY)); 51 message_ids.insert(std::make_pair( 52 "department", IDS_LIBADDRESSINPUT_DEPARTMENT)); 53 message_ids.insert(std::make_pair( 54 "district", IDS_LIBADDRESSINPUT_DISTRICT)); 55 message_ids.insert(std::make_pair( 56 "do_si", IDS_LIBADDRESSINPUT_DO_SI)); 57 message_ids.insert(std::make_pair( 58 "emirate", IDS_LIBADDRESSINPUT_EMIRATE)); 59 message_ids.insert(std::make_pair( 60 "island", IDS_LIBADDRESSINPUT_ISLAND)); 61 message_ids.insert(std::make_pair( 62 "oblast", IDS_LIBADDRESSINPUT_OBLAST)); 63 message_ids.insert(std::make_pair( 64 "parish", IDS_LIBADDRESSINPUT_PARISH)); 65 message_ids.insert(std::make_pair( 66 "prefecture", IDS_LIBADDRESSINPUT_PREFECTURE)); 67 message_ids.insert(std::make_pair( 68 "province", IDS_LIBADDRESSINPUT_PROVINCE)); 69 message_ids.insert(std::make_pair( 70 "state", IDS_LIBADDRESSINPUT_STATE)); 71 return message_ids; 72 } 73 74 const NameMessageIdMap& GetAdminAreaMessageIds() { 75 static const NameMessageIdMap kAdminAreaMessageIds(InitAdminAreaMessageIds()); 76 return kAdminAreaMessageIds; 77 } 78 79 NameMessageIdMap InitPostalCodeMessageIds() { 80 NameMessageIdMap message_ids; 81 message_ids.insert(std::make_pair( 82 "postal", IDS_LIBADDRESSINPUT_POSTAL_CODE_LABEL)); 83 message_ids.insert(std::make_pair( 84 "zip", IDS_LIBADDRESSINPUT_ZIP_CODE_LABEL)); 85 return message_ids; 86 } 87 88 const NameMessageIdMap& GetPostalCodeMessageIds() { 89 static const NameMessageIdMap kPostalCodeMessageIds( 90 InitPostalCodeMessageIds()); 91 return kPostalCodeMessageIds; 92 } 93 94 int GetMessageIdFromName(const std::string& name, 95 const NameMessageIdMap& message_ids) { 96 NameMessageIdMap::const_iterator it = message_ids.find(name); 97 return it != message_ids.end() ? it->second : INVALID_MESSAGE_ID; 98 } 99 100 // Determines whether a given string is a reg-exp or a string. We consider a 101 // string to be anything that doesn't contain characters with special meanings 102 // in regular expressions - (, [, \, {, ?. These special characters are all the 103 // ones that appear in the postal code regular expressions. 104 bool ContainsRegExSpecialCharacters(const std::string& input) { 105 return input.find_first_of("([\\{?") != std::string::npos; 106 } 107 108 } // namespace 109 110 Rule::Rule() 111 : id_(), 112 format_(), 113 latin_format_(), 114 required_(), 115 sub_keys_(), 116 languages_(), 117 postal_code_matcher_(NULL), 118 sole_postal_code_(), 119 admin_area_name_message_id_(INVALID_MESSAGE_ID), 120 postal_code_name_message_id_(INVALID_MESSAGE_ID), 121 name_(), 122 latin_name_(), 123 postal_code_example_(), 124 post_service_url_() {} 125 126 Rule::~Rule() {} 127 128 // static 129 const Rule& Rule::GetDefault() { 130 // Allocated once and leaked on shutdown. 131 static Rule* default_rule = NULL; 132 if (default_rule == NULL) { 133 default_rule = new Rule; 134 default_rule->ParseSerializedRule( 135 RegionDataConstants::GetDefaultRegionData()); 136 } 137 return *default_rule; 138 } 139 140 void Rule::CopyFrom(const Rule& rule) { 141 assert(this != &rule); 142 id_ = rule.id_; 143 format_ = rule.format_; 144 latin_format_ = rule.latin_format_; 145 required_ = rule.required_; 146 sub_keys_ = rule.sub_keys_; 147 languages_ = rule.languages_; 148 postal_code_matcher_.reset( 149 rule.postal_code_matcher_ == NULL 150 ? NULL 151 : new RE2ptr(new RE2(rule.postal_code_matcher_->ptr->pattern(), 152 rule.postal_code_matcher_->ptr->options()))); 153 sole_postal_code_ = rule.sole_postal_code_; 154 admin_area_name_message_id_ = rule.admin_area_name_message_id_; 155 postal_code_name_message_id_ = rule.postal_code_name_message_id_; 156 name_ = rule.name_; 157 latin_name_ = rule.latin_name_; 158 postal_code_example_ = rule.postal_code_example_; 159 post_service_url_ = rule.post_service_url_; 160 } 161 162 bool Rule::ParseSerializedRule(const std::string& serialized_rule) { 163 Json json; 164 if (!json.ParseObject(serialized_rule)) { 165 return false; 166 } 167 ParseJsonRule(json); 168 return true; 169 } 170 171 void Rule::ParseJsonRule(const Json& json) { 172 std::string value; 173 if (json.GetStringValueForKey("id", &value)) { 174 id_.swap(value); 175 } 176 177 if (json.GetStringValueForKey("fmt", &value)) { 178 ParseFormatRule(value, &format_); 179 } 180 181 if (json.GetStringValueForKey("lfmt", &value)) { 182 ParseFormatRule(value, &latin_format_); 183 } 184 185 if (json.GetStringValueForKey("require", &value)) { 186 ParseAddressFieldsRequired(value, &required_); 187 } 188 189 if (json.GetStringValueForKey("sub_keys", &value)) { 190 SplitString(value, kSeparator, &sub_keys_); 191 } 192 193 if (json.GetStringValueForKey("languages", &value)) { 194 SplitString(value, kSeparator, &languages_); 195 } 196 197 sole_postal_code_.clear(); 198 if (json.GetStringValueForKey("zip", &value)) { 199 // The "zip" field in the JSON data is used in two different ways to 200 // validate the postal code. At the country level, the "zip" field indicates 201 // a Java compatible regular expression corresponding to all postal codes in 202 // the country. At other levels, the regular expression indicates the postal 203 // code prefix expected for addresses in that region. 204 // 205 // In order to make the RE2 object created from the "zip" field useable for 206 // both these purposes, the pattern string is here prefixed with "^" to 207 // anchor it at the beginning of the string so that it can be used with 208 // RE2::PartialMatch() to perform prefix matching or else with 209 // RE2::FullMatch() to perform matching against the entire string. 210 RE2::Options options; 211 options.set_never_capture(true); 212 RE2* matcher = new RE2("^(" + value + ")", options); 213 if (matcher->ok()) { 214 postal_code_matcher_.reset(new RE2ptr(matcher)); 215 } else { 216 postal_code_matcher_.reset(NULL); 217 delete matcher; 218 } 219 // If the "zip" field is not a regular expression, then it is the sole 220 // postal code for this rule. 221 if (!ContainsRegExSpecialCharacters(value)) { 222 sole_postal_code_.swap(value); 223 } 224 } 225 226 if (json.GetStringValueForKey("state_name_type", &value)) { 227 admin_area_name_message_id_ = 228 GetMessageIdFromName(value, GetAdminAreaMessageIds()); 229 } 230 231 if (json.GetStringValueForKey("zip_name_type", &value)) { 232 postal_code_name_message_id_ = 233 GetMessageIdFromName(value, GetPostalCodeMessageIds()); 234 } 235 236 if (json.GetStringValueForKey("name", &value)) { 237 name_.swap(value); 238 } 239 240 if (json.GetStringValueForKey("lname", &value)) { 241 latin_name_.swap(value); 242 } 243 244 if (json.GetStringValueForKey("zipex", &value)) { 245 postal_code_example_.swap(value); 246 } 247 248 if (json.GetStringValueForKey("posturl", &value)) { 249 post_service_url_.swap(value); 250 } 251 } 252 253 } // namespace addressinput 254 } // namespace i18n 255