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