Home | History | Annotate | Download | only in src
      1 // Copyright 2015 The Weave Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/component_manager_impl.h"
      6 
      7 #include <base/strings/string_number_conversions.h>
      8 #include <base/strings/string_util.h>
      9 #include <base/strings/stringprintf.h>
     10 
     11 #include "src/commands/schema_constants.h"
     12 #include "src/json_error_codes.h"
     13 #include "src/string_utils.h"
     14 #include "src/utils.h"
     15 
     16 namespace weave {
     17 
     18 namespace {
     19 // Max of 100 state update events should be enough in the queue.
     20 const size_t kMaxStateChangeQueueSize = 100;
     21 
     22 const EnumToStringMap<UserRole>::Map kMap[] = {
     23     {UserRole::kViewer, commands::attributes::kCommand_Role_Viewer},
     24     {UserRole::kUser, commands::attributes::kCommand_Role_User},
     25     {UserRole::kOwner, commands::attributes::kCommand_Role_Owner},
     26     {UserRole::kManager, commands::attributes::kCommand_Role_Manager},
     27 };
     28 }  // anonymous namespace
     29 
     30 template <>
     31 LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
     32     : EnumToStringMap(kMap) {}
     33 
     34 ComponentManagerImpl::ComponentManagerImpl(provider::TaskRunner* task_runner,
     35                                            base::Clock* clock)
     36     : clock_{clock ? clock : &default_clock_},
     37       command_queue_{task_runner, clock_} {}
     38 
     39 ComponentManagerImpl::~ComponentManagerImpl() {}
     40 
     41 bool ComponentManagerImpl::AddComponent(const std::string& path,
     42                                         const std::string& name,
     43                                         const std::vector<std::string>& traits,
     44                                         ErrorPtr* error) {
     45   base::DictionaryValue* root = &components_;
     46   if (!path.empty()) {
     47     root = FindComponentGraftNode(path, error);
     48     if (!root)
     49       return false;
     50   }
     51   if (root->GetWithoutPathExpansion(name, nullptr)) {
     52     return Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidState,
     53                               "Component '%s' already exists at path '%s'",
     54                               name.c_str(), path.c_str());
     55   }
     56 
     57   // Check to make sure the declared traits are already defined.
     58   for (const std::string& trait : traits) {
     59     if (!FindTraitDefinition(trait)) {
     60       return Error::AddToPrintf(error, FROM_HERE,
     61                                 errors::commands::kInvalidPropValue,
     62                                 "Trait '%s' is undefined", trait.c_str());
     63     }
     64   }
     65   std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
     66   std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
     67   traits_list->AppendStrings(traits);
     68   dict->Set("traits", traits_list.release());
     69   root->SetWithoutPathExpansion(name, dict.release());
     70   for (const auto& cb : on_componet_tree_changed_)
     71     cb.Run();
     72   return true;
     73 }
     74 
     75 bool ComponentManagerImpl::AddComponentArrayItem(
     76     const std::string& path,
     77     const std::string& name,
     78     const std::vector<std::string>& traits,
     79     ErrorPtr* error) {
     80   base::DictionaryValue* root = &components_;
     81   if (!path.empty()) {
     82     root = FindComponentGraftNode(path, error);
     83     if (!root)
     84       return false;
     85   }
     86   base::ListValue* array_value = nullptr;
     87   if (!root->GetListWithoutPathExpansion(name, &array_value)) {
     88     array_value = new base::ListValue;
     89     root->SetWithoutPathExpansion(name, array_value);
     90   }
     91   std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
     92   std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
     93   traits_list->AppendStrings(traits);
     94   dict->Set("traits", traits_list.release());
     95   array_value->Append(dict.release());
     96   for (const auto& cb : on_componet_tree_changed_)
     97     cb.Run();
     98   return true;
     99 }
    100 
    101 bool ComponentManagerImpl::RemoveComponent(const std::string& path,
    102                                            const std::string& name,
    103                                            ErrorPtr* error) {
    104   base::DictionaryValue* root = &components_;
    105   if (!path.empty()) {
    106     root = FindComponentGraftNode(path, error);
    107     if (!root)
    108       return false;
    109   }
    110 
    111   if (!root->RemoveWithoutPathExpansion(name, nullptr)) {
    112     return Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidState,
    113                               "Component '%s' does not exist at path '%s'",
    114                               name.c_str(), path.c_str());
    115   }
    116 
    117   for (const auto& cb : on_componet_tree_changed_)
    118     cb.Run();
    119   return true;
    120 }
    121 
    122 bool ComponentManagerImpl::RemoveComponentArrayItem(const std::string& path,
    123                                                     const std::string& name,
    124                                                     size_t index,
    125                                                     ErrorPtr* error) {
    126   base::DictionaryValue* root = &components_;
    127   if (!path.empty()) {
    128     root = FindComponentGraftNode(path, error);
    129     if (!root)
    130       return false;
    131   }
    132 
    133   base::ListValue* array_value = nullptr;
    134   if (!root->GetListWithoutPathExpansion(name, &array_value)) {
    135     return Error::AddToPrintf(
    136         error, FROM_HERE, errors::commands::kInvalidState,
    137         "There is no component array named '%s' at path '%s'", name.c_str(),
    138         path.c_str());
    139   }
    140 
    141   if (!array_value->Remove(index, nullptr)) {
    142     return Error::AddToPrintf(
    143         error, FROM_HERE, errors::commands::kInvalidState,
    144         "Component array '%s' at path '%s' does not have an element %zu",
    145         name.c_str(), path.c_str(), index);
    146   }
    147 
    148   for (const auto& cb : on_componet_tree_changed_)
    149     cb.Run();
    150   return true;
    151 }
    152 
    153 void ComponentManagerImpl::AddComponentTreeChangedCallback(
    154     const base::Closure& callback) {
    155   on_componet_tree_changed_.push_back(callback);
    156   callback.Run();
    157 }
    158 
    159 bool ComponentManagerImpl::LoadTraits(const base::DictionaryValue& dict,
    160                                       ErrorPtr* error) {
    161   bool modified = false;
    162   bool result = true;
    163   // Check if any of the new traits are already defined. If so, make sure the
    164   // definition is exactly the same, or else this is an error.
    165   for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
    166     if (it.value().GetType() != base::Value::TYPE_DICTIONARY) {
    167       Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
    168                          "Trait '%s' must be an object", it.key().c_str());
    169       result = false;
    170       break;
    171     }
    172     const base::DictionaryValue* existing_def = nullptr;
    173     if (traits_.GetDictionary(it.key(), &existing_def)) {
    174       if (!existing_def->Equals(&it.value())) {
    175         Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
    176                            "Trait '%s' cannot be redefined", it.key().c_str());
    177         result = false;
    178         break;
    179       }
    180     } else {
    181       traits_.Set(it.key(), it.value().DeepCopy());
    182       modified = true;
    183     }
    184   }
    185 
    186   if (modified) {
    187     for (const auto& cb : on_trait_changed_)
    188       cb.Run();
    189   }
    190   return result;
    191 }
    192 
    193 bool ComponentManagerImpl::LoadTraits(const std::string& json,
    194                                       ErrorPtr* error) {
    195   std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
    196   if (!dict)
    197     return false;
    198   return LoadTraits(*dict, error);
    199 }
    200 
    201 void ComponentManagerImpl::AddTraitDefChangedCallback(
    202     const base::Closure& callback) {
    203   on_trait_changed_.push_back(callback);
    204   callback.Run();
    205 }
    206 
    207 void ComponentManagerImpl::AddCommand(
    208     std::unique_ptr<CommandInstance> command_instance) {
    209   command_queue_.Add(std::move(command_instance));
    210 }
    211 
    212 std::unique_ptr<CommandInstance> ComponentManagerImpl::ParseCommandInstance(
    213     const base::DictionaryValue& command,
    214     Command::Origin command_origin,
    215     UserRole role,
    216     std::string* id,
    217     ErrorPtr* error) {
    218   std::string command_id;
    219   auto command_instance =
    220       CommandInstance::FromJson(&command, command_origin, &command_id, error);
    221   // If we fail to validate the command definition, but there was a command ID
    222   // specified there, return it to the caller when requested. This will be
    223   // used to abort cloud commands.
    224   if (id)
    225     *id = command_id;
    226 
    227   if (!command_instance)
    228     return nullptr;
    229 
    230   UserRole minimal_role;
    231   if (!GetMinimalRole(command_instance->GetName(), &minimal_role, error))
    232     return nullptr;
    233 
    234   if (role < minimal_role) {
    235     return Error::AddToPrintf(error, FROM_HERE, "access_denied",
    236                               "User role '%s' less than minimal: '%s'",
    237                               EnumToString(role).c_str(),
    238                               EnumToString(minimal_role).c_str());
    239   }
    240 
    241   std::string component_path = command_instance->GetComponent();
    242   if (component_path.empty()) {
    243     // Find the component to which to route this command. Get the trait name
    244     // from the command name and find the first component that has this trait.
    245     auto trait_name =
    246         SplitAtFirst(command_instance->GetName(), ".", true).first;
    247     component_path = FindComponentWithTrait(trait_name);
    248     if (component_path.empty()) {
    249       return Error::AddToPrintf(
    250           error, FROM_HERE, "unrouted_command",
    251           "Unable route command '%s' because there is no component supporting"
    252           "trait '%s'",
    253           command_instance->GetName().c_str(), trait_name.c_str());
    254     }
    255     command_instance->SetComponent(component_path);
    256   }
    257 
    258   const base::DictionaryValue* component = FindComponent(component_path, error);
    259   if (!component)
    260     return nullptr;
    261 
    262   // Check that the command's trait is supported by the given component.
    263   auto pair = SplitAtFirst(command_instance->GetName(), ".", true);
    264 
    265   bool trait_supported = false;
    266   const base::ListValue* supported_traits = nullptr;
    267   if (component->GetList("traits", &supported_traits)) {
    268     for (const base::Value* value : *supported_traits) {
    269       std::string trait;
    270       CHECK(value->GetAsString(&trait));
    271       if (trait == pair.first) {
    272         trait_supported = true;
    273         break;
    274       }
    275     }
    276   }
    277 
    278   if (!trait_supported) {
    279     return Error::AddToPrintf(error, FROM_HERE, "trait_not_supported",
    280                               "Component '%s' doesn't support trait '%s'",
    281                               component_path.c_str(), pair.first.c_str());
    282   }
    283 
    284   if (command_id.empty()) {
    285     command_id = std::to_string(++next_command_id_);
    286     command_instance->SetID(command_id);
    287     if (id)
    288       *id = command_id;
    289   }
    290 
    291   return command_instance;
    292 }
    293 
    294 CommandInstance* ComponentManagerImpl::FindCommand(const std::string& id) {
    295   return command_queue_.Find(id);
    296 }
    297 
    298 void ComponentManagerImpl::AddCommandAddedCallback(
    299     const CommandQueue::CommandCallback& callback) {
    300   command_queue_.AddCommandAddedCallback(callback);
    301 }
    302 
    303 void ComponentManagerImpl::AddCommandRemovedCallback(
    304     const CommandQueue::CommandCallback& callback) {
    305   command_queue_.AddCommandRemovedCallback(callback);
    306 }
    307 
    308 void ComponentManagerImpl::AddCommandHandler(
    309     const std::string& component_path,
    310     const std::string& command_name,
    311     const Device::CommandHandlerCallback& callback) {
    312   // If both component_path and command_name are empty, we are adding the
    313   // default handler for all commands.
    314   if (!component_path.empty() || !command_name.empty()) {
    315     CHECK(FindCommandDefinition(command_name)) << "Command undefined: "
    316                                                << command_name;
    317   }
    318   command_queue_.AddCommandHandler(component_path, command_name, callback);
    319 }
    320 
    321 const base::DictionaryValue* ComponentManagerImpl::FindComponent(
    322     const std::string& path,
    323     ErrorPtr* error) const {
    324   return FindComponentAt(&components_, path, error);
    325 }
    326 
    327 const base::DictionaryValue* ComponentManagerImpl::FindTraitDefinition(
    328     const std::string& name) const {
    329   const base::DictionaryValue* trait = nullptr;
    330   traits_.GetDictionaryWithoutPathExpansion(name, &trait);
    331   return trait;
    332 }
    333 
    334 const base::DictionaryValue* ComponentManagerImpl::FindCommandDefinition(
    335     const std::string& command_name) const {
    336   const base::DictionaryValue* definition = nullptr;
    337   std::vector<std::string> components = Split(command_name, ".", true, false);
    338   // Make sure the |command_name| came in form of trait_name.command_name.
    339   if (components.size() != 2)
    340     return definition;
    341   std::string key = base::StringPrintf("%s.commands.%s", components[0].c_str(),
    342                                        components[1].c_str());
    343   traits_.GetDictionary(key, &definition);
    344   return definition;
    345 }
    346 
    347 bool ComponentManagerImpl::GetMinimalRole(const std::string& command_name,
    348                                           UserRole* minimal_role,
    349                                           ErrorPtr* error) const {
    350   const base::DictionaryValue* command = FindCommandDefinition(command_name);
    351   if (!command) {
    352     return Error::AddToPrintf(
    353         error, FROM_HERE, errors::commands::kInvalidCommandName,
    354         "Command definition for '%s' not found", command_name.c_str());
    355   }
    356   std::string value;
    357   // The JSON definition has been pre-validated already in LoadCommands, so
    358   // just using CHECKs here.
    359   CHECK(command->GetString(commands::attributes::kCommand_Role, &value));
    360   CHECK(StringToEnum(value, minimal_role));
    361   return true;
    362 }
    363 
    364 void ComponentManagerImpl::AddStateChangedCallback(
    365     const base::Closure& callback) {
    366   on_state_changed_.push_back(callback);
    367   callback.Run();  // Force to read current state.
    368 }
    369 
    370 bool ComponentManagerImpl::SetStateProperties(const std::string& component_path,
    371                                               const base::DictionaryValue& dict,
    372                                               ErrorPtr* error) {
    373   base::DictionaryValue* component =
    374       FindMutableComponent(component_path, error);
    375   if (!component)
    376     return false;
    377 
    378   base::DictionaryValue* state = nullptr;
    379   if (!component->GetDictionary("state", &state)) {
    380     state = new base::DictionaryValue;
    381     component->Set("state", state);
    382   }
    383   state->MergeDictionary(&dict);
    384   last_state_change_id_++;
    385   auto& queue = state_change_queues_[component_path];
    386   if (!queue)
    387     queue.reset(new StateChangeQueue{kMaxStateChangeQueueSize});
    388   base::Time timestamp = clock_->Now();
    389   queue->NotifyPropertiesUpdated(timestamp, dict);
    390   for (const auto& cb : on_state_changed_)
    391     cb.Run();
    392   return true;
    393 }
    394 
    395 bool ComponentManagerImpl::SetStatePropertiesFromJson(
    396     const std::string& component_path,
    397     const std::string& json,
    398     ErrorPtr* error) {
    399   std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
    400   return dict && SetStateProperties(component_path, *dict, error);
    401 }
    402 
    403 const base::Value* ComponentManagerImpl::GetStateProperty(
    404     const std::string& component_path,
    405     const std::string& name,
    406     ErrorPtr* error) const {
    407   const base::DictionaryValue* component = FindComponent(component_path, error);
    408   if (!component)
    409     return nullptr;
    410   auto pair = SplitAtFirst(name, ".", true);
    411   if (pair.first.empty()) {
    412     return Error::AddToPrintf(error, FROM_HERE,
    413                               errors::commands::kPropertyMissing,
    414                               "Empty state package in '%s'", name.c_str());
    415   }
    416   if (pair.second.empty()) {
    417     return Error::AddToPrintf(
    418         error, FROM_HERE, errors::commands::kPropertyMissing,
    419         "State property name not specified in '%s'", name.c_str());
    420   }
    421   std::string key = base::StringPrintf("state.%s", name.c_str());
    422   const base::Value* value = nullptr;
    423   if (!component->Get(key, &value)) {
    424     return Error::AddToPrintf(error, FROM_HERE,
    425                               errors::commands::kPropertyMissing,
    426                               "State property '%s' not found in component '%s'",
    427                               name.c_str(), component_path.c_str());
    428   }
    429   return value;
    430 }
    431 
    432 bool ComponentManagerImpl::SetStateProperty(const std::string& component_path,
    433                                             const std::string& name,
    434                                             const base::Value& value,
    435                                             ErrorPtr* error) {
    436   base::DictionaryValue dict;
    437   auto pair = SplitAtFirst(name, ".", true);
    438   if (pair.first.empty()) {
    439     return Error::AddToPrintf(error, FROM_HERE,
    440                               errors::commands::kPropertyMissing,
    441                               "Empty state package in '%s'", name.c_str());
    442   }
    443   if (pair.second.empty()) {
    444     return Error::AddToPrintf(
    445         error, FROM_HERE, errors::commands::kPropertyMissing,
    446         "State property name not specified in '%s'", name.c_str());
    447   }
    448   dict.Set(name, value.DeepCopy());
    449   return SetStateProperties(component_path, dict, error);
    450 }
    451 
    452 ComponentManager::StateSnapshot
    453 ComponentManagerImpl::GetAndClearRecordedStateChanges() {
    454   StateSnapshot snapshot;
    455   snapshot.update_id = GetLastStateChangeId();
    456   for (auto& pair : state_change_queues_) {
    457     auto changes = pair.second->GetAndClearRecordedStateChanges();
    458     auto component = pair.first;
    459     auto conv = [component](weave::StateChange& change) {
    460       return ComponentStateChange{change.timestamp, component,
    461                                   std::move(change.changed_properties)};
    462     };
    463     std::transform(changes.begin(), changes.end(),
    464                    std::back_inserter(snapshot.state_changes), conv);
    465   }
    466 
    467   // Sort events by the timestamp.
    468   auto pred = [](const ComponentStateChange& lhs,
    469                  const ComponentStateChange& rhs) {
    470     return lhs.timestamp < rhs.timestamp;
    471   };
    472   std::sort(snapshot.state_changes.begin(), snapshot.state_changes.end(), pred);
    473   state_change_queues_.clear();
    474   return snapshot;
    475 }
    476 
    477 void ComponentManagerImpl::NotifyStateUpdatedOnServer(UpdateID id) {
    478   on_server_state_updated_.Notify(id);
    479 }
    480 
    481 ComponentManager::Token ComponentManagerImpl::AddServerStateUpdatedCallback(
    482     const base::Callback<void(UpdateID)>& callback) {
    483   if (state_change_queues_.empty())
    484     callback.Run(GetLastStateChangeId());
    485   return Token{on_server_state_updated_.Add(callback).release()};
    486 }
    487 
    488 std::string ComponentManagerImpl::FindComponentWithTrait(
    489     const std::string& trait) const {
    490   for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
    491        it.Advance()) {
    492     const base::ListValue* supported_traits = nullptr;
    493     const base::DictionaryValue* component = nullptr;
    494     CHECK(it.value().GetAsDictionary(&component));
    495     if (component->GetList("traits", &supported_traits)) {
    496       for (const base::Value* value : *supported_traits) {
    497         std::string supported_trait;
    498         CHECK(value->GetAsString(&supported_trait));
    499         if (trait == supported_trait)
    500           return it.key();
    501       }
    502     }
    503   }
    504   return std::string{};
    505 }
    506 
    507 bool ComponentManagerImpl::AddLegacyCommandDefinitions(
    508     const base::DictionaryValue& dict,
    509     ErrorPtr* error) {
    510   bool result = true;
    511   bool modified = false;
    512   for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
    513     const base::DictionaryValue* command_dict = nullptr;
    514     if (!it.value().GetAsDictionary(&command_dict)) {
    515       Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
    516                          "Package '%s' must be an object", it.key().c_str());
    517       result = false;
    518       continue;
    519     }
    520     AddTraitToLegacyComponent(it.key());
    521     for (base::DictionaryValue::Iterator it_def(*command_dict);
    522          !it_def.IsAtEnd(); it_def.Advance()) {
    523       std::string key = base::StringPrintf("%s.commands.%s", it.key().c_str(),
    524                                            it_def.key().c_str());
    525       if (traits_.GetDictionary(key, nullptr)) {
    526         Error::AddToPrintf(error, FROM_HERE,
    527                            errors::commands::kInvalidPropValue,
    528                            "Redefining command '%s.%s'", it.key().c_str(),
    529                            it_def.key().c_str());
    530         result = false;
    531         continue;
    532       }
    533       traits_.Set(key, it_def.value().DeepCopy());
    534       modified = true;
    535     }
    536   }
    537 
    538   if (modified) {
    539     for (const auto& cb : on_trait_changed_)
    540       cb.Run();
    541   }
    542   return result;
    543 }
    544 
    545 bool ComponentManagerImpl::AddLegacyStateDefinitions(
    546     const base::DictionaryValue& dict,
    547     ErrorPtr* error) {
    548   bool result = true;
    549   bool modified = false;
    550   for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
    551     const base::DictionaryValue* state_dict = nullptr;
    552     if (!it.value().GetAsDictionary(&state_dict)) {
    553       Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
    554                          "Package '%s' must be an object", it.key().c_str());
    555       result = false;
    556       continue;
    557     }
    558     AddTraitToLegacyComponent(it.key());
    559     for (base::DictionaryValue::Iterator it_def(*state_dict); !it_def.IsAtEnd();
    560          it_def.Advance()) {
    561       std::string key = base::StringPrintf("%s.state.%s", it.key().c_str(),
    562                                            it_def.key().c_str());
    563       if (traits_.GetDictionary(key, nullptr)) {
    564         Error::AddToPrintf(error, FROM_HERE,
    565                            errors::commands::kInvalidPropValue,
    566                            "Redefining state property '%s.%s'",
    567                            it.key().c_str(), it_def.key().c_str());
    568         result = false;
    569         continue;
    570       }
    571       traits_.Set(key, it_def.value().DeepCopy());
    572       modified = true;
    573     }
    574   }
    575 
    576   if (modified) {
    577     for (const auto& cb : on_trait_changed_)
    578       cb.Run();
    579   }
    580   return result;
    581 }
    582 
    583 const base::DictionaryValue& ComponentManagerImpl::GetLegacyState() const {
    584   legacy_state_.Clear();
    585   // Build state from components.
    586   for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
    587        it.Advance()) {
    588     const base::DictionaryValue* component_dict = nullptr;
    589     const base::DictionaryValue* component_state = nullptr;
    590     if (it.value().GetAsDictionary(&component_dict) &&
    591         component_dict->GetDictionary("state", &component_state)) {
    592       legacy_state_.MergeDictionary(component_state);
    593     }
    594   }
    595   return legacy_state_;
    596 }
    597 
    598 const base::DictionaryValue& ComponentManagerImpl::GetLegacyCommandDefinitions()
    599     const {
    600   legacy_command_defs_.Clear();
    601   // Build commandDefs from traits.
    602   for (base::DictionaryValue::Iterator it(traits_); !it.IsAtEnd();
    603        it.Advance()) {
    604     const base::DictionaryValue* trait_dict = nullptr;
    605     const base::DictionaryValue* trait_commands = nullptr;
    606     if (it.value().GetAsDictionary(&trait_dict) &&
    607         trait_dict->GetDictionary("commands", &trait_commands)) {
    608       base::DictionaryValue dict;
    609       dict.Set(it.key(), trait_commands->DeepCopy());
    610       legacy_command_defs_.MergeDictionary(&dict);
    611     }
    612   }
    613   return legacy_command_defs_;
    614 }
    615 
    616 void ComponentManagerImpl::AddTraitToLegacyComponent(const std::string& trait) {
    617   // First check if we already have a component supporting this trait.
    618   if (!FindComponentWithTrait(trait).empty())
    619     return;
    620 
    621   // If not, add this trait to the first component available.
    622   base::DictionaryValue* component = nullptr;
    623   base::DictionaryValue::Iterator it(components_);
    624   if (it.IsAtEnd()) {
    625     // No components at all. Create a new one with dummy name.
    626     // This normally wouldn't happen since libweave creates its own component
    627     // at startup.
    628     component = new base::DictionaryValue;
    629     components_.Set("__weave__", component);
    630   } else {
    631     CHECK(components_.GetDictionary(it.key(), &component));
    632   }
    633   base::ListValue* traits = nullptr;
    634   if (!component->GetList("traits", &traits)) {
    635     traits = new base::ListValue;
    636     component->Set("traits", traits);
    637   }
    638   traits->AppendString(trait);
    639 }
    640 
    641 base::DictionaryValue* ComponentManagerImpl::FindComponentGraftNode(
    642     const std::string& path,
    643     ErrorPtr* error) {
    644   base::DictionaryValue* root = nullptr;
    645   base::DictionaryValue* component = FindMutableComponent(path, error);
    646   if (component && !component->GetDictionary("components", &root)) {
    647     root = new base::DictionaryValue;
    648     component->Set("components", root);
    649   }
    650   return root;
    651 }
    652 
    653 base::DictionaryValue* ComponentManagerImpl::FindMutableComponent(
    654     const std::string& path,
    655     ErrorPtr* error) {
    656   return const_cast<base::DictionaryValue*>(
    657       FindComponentAt(&components_, path, error));
    658 }
    659 
    660 const base::DictionaryValue* ComponentManagerImpl::FindComponentAt(
    661     const base::DictionaryValue* root,
    662     const std::string& path,
    663     ErrorPtr* error) {
    664   auto parts = Split(path, ".", true, false);
    665   std::string root_path;
    666   for (size_t i = 0; i < parts.size(); i++) {
    667     auto element = SplitAtFirst(parts[i], "[", true);
    668     int array_index = -1;
    669     if (element.first.empty()) {
    670       return Error::AddToPrintf(
    671           error, FROM_HERE, errors::commands::kPropertyMissing,
    672           "Empty path element at '%s'", root_path.c_str());
    673     }
    674     if (!element.second.empty()) {
    675       if (element.second.back() != ']') {
    676         return Error::AddToPrintf(
    677             error, FROM_HERE, errors::commands::kPropertyMissing,
    678             "Invalid array element syntax '%s'", parts[i].c_str());
    679       }
    680       element.second.pop_back();
    681       std::string index_str;
    682       base::TrimWhitespaceASCII(element.second, base::TrimPositions::TRIM_ALL,
    683                                 &index_str);
    684       if (!base::StringToInt(index_str, &array_index) || array_index < 0) {
    685         return Error::AddToPrintf(
    686             error, FROM_HERE, errors::commands::kInvalidPropValue,
    687             "Invalid array index '%s'", element.second.c_str());
    688       }
    689     }
    690 
    691     if (!root_path.empty()) {
    692       // We have processed at least one item in the path before, so now |root|
    693       // points to the actual parent component. We need the root to point to
    694       // the 'components' element containing child sub-components instead.
    695       if (!root->GetDictionary("components", &root)) {
    696         return Error::AddToPrintf(error, FROM_HERE,
    697                                   errors::commands::kPropertyMissing,
    698                                   "Component '%s' does not exist at '%s'",
    699                                   element.first.c_str(), root_path.c_str());
    700       }
    701     }
    702 
    703     const base::Value* value = nullptr;
    704     if (!root->GetWithoutPathExpansion(element.first, &value)) {
    705       Error::AddToPrintf(error, FROM_HERE, errors::commands::kPropertyMissing,
    706                          "Component '%s' does not exist at '%s'",
    707                          element.first.c_str(), root_path.c_str());
    708       return nullptr;
    709     }
    710 
    711     if (value->GetType() == base::Value::TYPE_LIST && array_index < 0) {
    712       return Error::AddToPrintf(error, FROM_HERE,
    713                                 errors::commands::kTypeMismatch,
    714                                 "Element '%s.%s' is an array",
    715                                 root_path.c_str(), element.first.c_str());
    716     }
    717     if (value->GetType() == base::Value::TYPE_DICTIONARY && array_index >= 0) {
    718       return Error::AddToPrintf(error, FROM_HERE,
    719                                 errors::commands::kTypeMismatch,
    720                                 "Element '%s.%s' is not an array",
    721                                 root_path.c_str(), element.first.c_str());
    722     }
    723 
    724     if (value->GetType() == base::Value::TYPE_DICTIONARY) {
    725       CHECK(value->GetAsDictionary(&root));
    726     } else {
    727       const base::ListValue* component_array = nullptr;
    728       CHECK(value->GetAsList(&component_array));
    729       const base::Value* component_value = nullptr;
    730       if (!component_array->Get(array_index, &component_value) ||
    731           !component_value->GetAsDictionary(&root)) {
    732         return Error::AddToPrintf(
    733             error, FROM_HERE, errors::commands::kPropertyMissing,
    734             "Element '%s.%s' does not contain item #%d", root_path.c_str(),
    735             element.first.c_str(), array_index);
    736       }
    737     }
    738     if (!root_path.empty())
    739       root_path += '.';
    740     root_path += parts[i];
    741   }
    742   return root;
    743 }
    744 
    745 }  // namespace weave
    746