policy_map.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "components/policy/core/common/policy_map.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 97dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "base/callback.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace policy { 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PolicyMap::Entry::Entry() 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : level(POLICY_LEVEL_RECOMMENDED), 165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scope(POLICY_SCOPE_USER), 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) value(NULL), 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) external_data_fetcher(NULL) {} 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void PolicyMap::Entry::DeleteOwnedMembers() { 21a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) delete value; 22a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) value = NULL; 23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) delete external_data_fetcher; 24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) external_data_fetcher = NULL; 25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)scoped_ptr<PolicyMap::Entry> PolicyMap::Entry::DeepCopy() const { 28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<Entry> copy(new Entry); 29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) copy->level = level; 30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) copy->scope = scope; 31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (value) 32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) copy->value = value->DeepCopy(); 33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (external_data_fetcher) { 34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) copy->external_data_fetcher = 35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) new ExternalDataFetcher(*external_data_fetcher); 36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return copy.Pass(); 38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PolicyMap::Entry::has_higher_priority_than( 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const PolicyMap::Entry& other) const { 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (level == other.level) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return scope > other.scope; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return level > other.level; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PolicyMap::Entry::Equals(const PolicyMap::Entry& other) const { 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return level == other.level && 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scope == other.scope && 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Value::Equals(value, other.value) && 527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch ExternalDataFetcher::Equals(external_data_fetcher, 537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch other.external_data_fetcher); 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PolicyMap::PolicyMap() { 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PolicyMap::~PolicyMap() { 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Clear(); 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const PolicyMap::Entry* PolicyMap::Get(const std::string& policy) const { 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyMapType::const_iterator entry = map_.find(policy); 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return entry == map_.end() ? NULL : &entry->second; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const base::Value* PolicyMap::GetValue(const std::string& policy) const { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyMapType::const_iterator entry = map_.find(policy); 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return entry == map_.end() ? NULL : entry->second.value; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::Set(const std::string& policy, 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyLevel level, 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyScope scope, 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Value* value, 777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch ExternalDataFetcher* external_data_fetcher) { 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Entry& entry = map_[policy]; 79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) entry.DeleteOwnedMembers(); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entry.level = level; 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entry.scope = scope; 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entry.value = value; 837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch entry.external_data_fetcher = external_data_fetcher; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::Erase(const std::string& policy) { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyMapType::iterator it = map_.find(policy); 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it != map_.end()) { 89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) it->second.DeleteOwnedMembers(); 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) map_.erase(it); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::Swap(PolicyMap* other) { 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) map_.swap(other->map_); 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::CopyFrom(const PolicyMap& other) { 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Clear(); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (const_iterator it = other.begin(); it != other.end(); ++it) { 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Entry& entry = it->second; 1027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch Set(it->first, entry.level, entry.scope, 1037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch entry.value->DeepCopy(), entry.external_data_fetcher ? 1047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch new ExternalDataFetcher(*entry.external_data_fetcher) : NULL); 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)scoped_ptr<PolicyMap> PolicyMap::DeepCopy() const { 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PolicyMap* copy = new PolicyMap(); 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) copy->CopyFrom(*this); 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return make_scoped_ptr(copy); 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::MergeFrom(const PolicyMap& other) { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (const_iterator it = other.begin(); it != other.end(); ++it) { 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Entry* entry = Get(it->first); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!entry || it->second.has_higher_priority_than(*entry)) { 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Set(it->first, it->second.level, it->second.scope, 1197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch it->second.value->DeepCopy(), it->second.external_data_fetcher ? 1207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch new ExternalDataFetcher(*it->second.external_data_fetcher) : 1217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch NULL); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::LoadFrom( 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::DictionaryValue* policies, 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyLevel level, 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyScope scope) { 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (base::DictionaryValue::Iterator it(*policies); 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) !it.IsAtEnd(); it.Advance()) { 1327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch Set(it.key(), level, scope, it.value().DeepCopy(), NULL); 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::GetDifferingKeys(const PolicyMap& other, 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::set<std::string>* differing_keys) const { 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Walk over the maps in lockstep, adding everything that is different. 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const_iterator iter_this(begin()); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const_iterator iter_other(other.begin()); 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (iter_this != end() && iter_other != other.end()) { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int diff = iter_this->first.compare(iter_other->first); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (diff == 0) { 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!iter_this->second.Equals(iter_other->second)) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) differing_keys->insert(iter_this->first); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++iter_this; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++iter_other; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (diff < 0) { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) differing_keys->insert(iter_this->first); 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++iter_this; 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) differing_keys->insert(iter_other->first); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++iter_other; 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Add the remaining entries. 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for ( ; iter_this != end(); ++iter_this) 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) differing_keys->insert(iter_this->first); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for ( ; iter_other != other.end(); ++iter_other) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) differing_keys->insert(iter_other->first); 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::FilterLevel(PolicyLevel level) { 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PolicyMapType::iterator iter(map_.begin()); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (iter != map_.end()) { 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (iter->second.level != level) { 168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) iter->second.DeleteOwnedMembers(); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) map_.erase(iter++); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ++iter; 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PolicyMap::Equals(const PolicyMap& other) const { 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return other.size() == size() && 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::equal(begin(), end(), other.begin(), MapEntryEquals); 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PolicyMap::empty() const { 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return map_.empty(); 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t PolicyMap::size() const { 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return map_.size(); 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PolicyMap::const_iterator PolicyMap::begin() const { 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return map_.begin(); 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PolicyMap::const_iterator PolicyMap::end() const { 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return map_.end(); 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PolicyMap::Clear() { 198a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) for (PolicyMapType::iterator it = map_.begin(); it != map_.end(); ++it) 199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) it->second.DeleteOwnedMembers(); 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) map_.clear(); 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PolicyMap::MapEntryEquals(const PolicyMap::PolicyMapType::value_type& a, 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const PolicyMap::PolicyMapType::value_type& b) { 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return a.first == b.first && a.second.Equals(b.second); 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace policy 210