1 // Copyright (C) 2014 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include "ondemand_supply_task.h"
17 #include <libaddressinput/address_field.h>
18 #include <libaddressinput/callback.h>
19 #include <libaddressinput/supplier.h>
20 #include <libaddressinput/util/basictypes.h>
30 #include "lookup_key.h"
31 #include "retriever.h"
35 namespace addressinput {
37 OndemandSupplyTask::OndemandSupplyTask(
38 const LookupKey& lookup_key,
39 std::map<std::string, const Rule*>* rules,
40 const Supplier::Callback& supplied)
43 lookup_key_(lookup_key),
46 retrieved_(BuildCallback(this, &OndemandSupplyTask::Load)),
48 assert(rule_cache_ != NULL);
49 assert(retrieved_ != NULL);
52 OndemandSupplyTask::~OndemandSupplyTask() {
55 void OndemandSupplyTask::Queue(const std::string& key) {
56 assert(pending_.find(key) == pending_.end());
60 void OndemandSupplyTask::Retrieve(const Retriever& retriever) {
61 if (pending_.empty()) {
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.
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_);
81 void 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));
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.
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.
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());
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.
117 // Pointer to object in the map.
118 hierarchy_.rule[depth] = result.first->second;
128 if (pending_.empty()) {
133 void OndemandSupplyTask::Loaded() {
134 supplied_(success_, lookup_key_, hierarchy_);
138 } // namespace addressinput