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
16namespace weave {
17
18namespace {
19// Max of 100 state update events should be enough in the queue.
20const size_t kMaxStateChangeQueueSize = 100;
21
22const 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
30template <>
31LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
32    : EnumToStringMap(kMap) {}
33
34ComponentManagerImpl::ComponentManagerImpl(provider::TaskRunner* task_runner,
35                                           base::Clock* clock)
36    : clock_{clock ? clock : &default_clock_},
37      command_queue_{task_runner, clock_} {}
38
39ComponentManagerImpl::~ComponentManagerImpl() {}
40
41bool 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
75bool 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
101bool 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
122bool 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
153void ComponentManagerImpl::AddComponentTreeChangedCallback(
154    const base::Closure& callback) {
155  on_componet_tree_changed_.push_back(callback);
156  callback.Run();
157}
158
159bool 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
193bool 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
201void ComponentManagerImpl::AddTraitDefChangedCallback(
202    const base::Closure& callback) {
203  on_trait_changed_.push_back(callback);
204  callback.Run();
205}
206
207void ComponentManagerImpl::AddCommand(
208    std::unique_ptr<CommandInstance> command_instance) {
209  command_queue_.Add(std::move(command_instance));
210}
211
212std::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
294CommandInstance* ComponentManagerImpl::FindCommand(const std::string& id) {
295  return command_queue_.Find(id);
296}
297
298void ComponentManagerImpl::AddCommandAddedCallback(
299    const CommandQueue::CommandCallback& callback) {
300  command_queue_.AddCommandAddedCallback(callback);
301}
302
303void ComponentManagerImpl::AddCommandRemovedCallback(
304    const CommandQueue::CommandCallback& callback) {
305  command_queue_.AddCommandRemovedCallback(callback);
306}
307
308void 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
321const base::DictionaryValue* ComponentManagerImpl::FindComponent(
322    const std::string& path,
323    ErrorPtr* error) const {
324  return FindComponentAt(&components_, path, error);
325}
326
327const 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
334const 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
347bool 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
364void ComponentManagerImpl::AddStateChangedCallback(
365    const base::Closure& callback) {
366  on_state_changed_.push_back(callback);
367  callback.Run();  // Force to read current state.
368}
369
370bool 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
395bool 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
403const 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
432bool 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
452ComponentManager::StateSnapshot
453ComponentManagerImpl::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
477void ComponentManagerImpl::NotifyStateUpdatedOnServer(UpdateID id) {
478  on_server_state_updated_.Notify(id);
479}
480
481ComponentManager::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
488std::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
507bool 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
545bool 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
583const 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
598const 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
616void 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
641base::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
653base::DictionaryValue* ComponentManagerImpl::FindMutableComponent(
654    const std::string& path,
655    ErrorPtr* error) {
656  return const_cast<base::DictionaryValue*>(
657      FindComponentAt(&components_, path, error));
658}
659
660const 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