Home | History | Annotate | Download | only in src
      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