1// Copyright (C) 2014 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 "ondemand_supply_task.h" 16 17#include <libaddressinput/address_field.h> 18#include <libaddressinput/callback.h> 19#include <libaddressinput/supplier.h> 20#include <libaddressinput/util/basictypes.h> 21 22#include <algorithm> 23#include <cassert> 24#include <cstddef> 25#include <map> 26#include <set> 27#include <string> 28#include <utility> 29 30#include "lookup_key.h" 31#include "retriever.h" 32#include "rule.h" 33 34namespace i18n { 35namespace addressinput { 36 37OndemandSupplyTask::OndemandSupplyTask( 38 const LookupKey& lookup_key, 39 std::map<std::string, const Rule*>* rules, 40 const Supplier::Callback& supplied) 41 : hierarchy_(), 42 pending_(), 43 lookup_key_(lookup_key), 44 rule_cache_(rules), 45 supplied_(supplied), 46 retrieved_(BuildCallback(this, &OndemandSupplyTask::Load)), 47 success_(true) { 48 assert(rule_cache_ != NULL); 49 assert(retrieved_ != NULL); 50} 51 52OndemandSupplyTask::~OndemandSupplyTask() { 53} 54 55void OndemandSupplyTask::Queue(const std::string& key) { 56 assert(pending_.find(key) == pending_.end()); 57 pending_.insert(key); 58} 59 60void OndemandSupplyTask::Retrieve(const Retriever& retriever) { 61 if (pending_.empty()) { 62 Loaded(); 63 } else { 64 // When the final pending rule has been retrieved, the retrieved_ callback, 65 // implemented by Load(), will finish by calling Loaded(), which will finish 66 // by delete'ing this OndemandSupplyTask object. So after the final call to 67 // retriever.Retrieve() no attributes of this object can be accessed (as the 68 // object then no longer will exist, if the final callback has finished by 69 // then), and the condition statement of the loop must therefore not use the 70 // otherwise obvious it != pending_.end() but instead test a local variable 71 // that isn't affected by the object being deleted. 72 bool done = false; 73 for (std::set<std::string>::const_iterator it = pending_.begin(); !done; ) { 74 const std::string& key = *it++; 75 done = it == pending_.end(); 76 retriever.Retrieve(key, *retrieved_); 77 } 78 } 79} 80 81void OndemandSupplyTask::Load(bool success, 82 const std::string& key, 83 const std::string& data) { 84 size_t depth = std::count(key.begin(), key.end(), '/') - 1; 85 assert(depth < arraysize(LookupKey::kHierarchy)); 86 87 // Sanity check: This key should be present in the set of pending requests. 88 size_t status = pending_.erase(key); 89 assert(status == 1); // There will always be one item erased from the set. 90 (void)status; // Prevent unused variable if assert() is optimized away. 91 92 if (success) { 93 // The address metadata server will return the empty JSON "{}" when it 94 // successfully performed a lookup, but didn't find any data for that key. 95 if (data != "{}") { 96 Rule* rule = new Rule; 97 if (LookupKey::kHierarchy[depth] == COUNTRY) { 98 // All rules on the COUNTRY level inherit from the default rule. 99 rule->CopyFrom(Rule::GetDefault()); 100 } 101 if (rule->ParseSerializedRule(data)) { 102 // Try inserting the Rule object into the rule_cache_ map, or else find 103 // the already existing Rule object with the same ID already in the map. 104 // It is possible that a key was queued even though the corresponding 105 // Rule object is already in the cache, as the data server is free to do 106 // advanced normalization and aliasing so that the ID of the data 107 // returned is different from the key requested. (It would be possible 108 // to cache such aliases, to increase performance in the case where a 109 // certain alias is requested repeatedly, but such a cache would then 110 // have to be kept to some limited size to not grow indefinitely with 111 // every possible permutation of a name recognized by the data server.) 112 std::pair<std::map<std::string, const Rule*>::iterator, bool> result = 113 rule_cache_->insert(std::make_pair(rule->GetId(), rule)); 114 if (!result.second) { // There was already an entry with this ID. 115 delete rule; 116 } 117 // Pointer to object in the map. 118 hierarchy_.rule[depth] = result.first->second; 119 } else { 120 delete rule; 121 success_ = false; 122 } 123 } 124 } else { 125 success_ = false; 126 } 127 128 if (pending_.empty()) { 129 Loaded(); 130 } 131} 132 133void OndemandSupplyTask::Loaded() { 134 supplied_(success_, lookup_key_, hierarchy_); 135 delete this; 136} 137 138} // namespace addressinput 139} // namespace i18n 140