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 <>
EnumToStringMap()31 LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
32 : EnumToStringMap(kMap) {}
33
ComponentManagerImpl(provider::TaskRunner * task_runner,base::Clock * clock)34 ComponentManagerImpl::ComponentManagerImpl(provider::TaskRunner* task_runner,
35 base::Clock* clock)
36 : clock_{clock ? clock : &default_clock_},
37 command_queue_{task_runner, clock_} {}
38
~ComponentManagerImpl()39 ComponentManagerImpl::~ComponentManagerImpl() {}
40
AddComponent(const std::string & path,const std::string & name,const std::vector<std::string> & traits,ErrorPtr * error)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
AddComponentArrayItem(const std::string & path,const std::string & name,const std::vector<std::string> & traits,ErrorPtr * error)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
RemoveComponent(const std::string & path,const std::string & name,ErrorPtr * error)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
RemoveComponentArrayItem(const std::string & path,const std::string & name,size_t index,ErrorPtr * error)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
AddComponentTreeChangedCallback(const base::Closure & callback)153 void ComponentManagerImpl::AddComponentTreeChangedCallback(
154 const base::Closure& callback) {
155 on_componet_tree_changed_.push_back(callback);
156 callback.Run();
157 }
158
LoadTraits(const base::DictionaryValue & dict,ErrorPtr * error)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
LoadTraits(const std::string & json,ErrorPtr * error)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
AddTraitDefChangedCallback(const base::Closure & callback)201 void ComponentManagerImpl::AddTraitDefChangedCallback(
202 const base::Closure& callback) {
203 on_trait_changed_.push_back(callback);
204 callback.Run();
205 }
206
AddCommand(std::unique_ptr<CommandInstance> command_instance)207 void ComponentManagerImpl::AddCommand(
208 std::unique_ptr<CommandInstance> command_instance) {
209 command_queue_.Add(std::move(command_instance));
210 }
211
ParseCommandInstance(const base::DictionaryValue & command,Command::Origin command_origin,UserRole role,std::string * id,ErrorPtr * error)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
FindCommand(const std::string & id)294 CommandInstance* ComponentManagerImpl::FindCommand(const std::string& id) {
295 return command_queue_.Find(id);
296 }
297
AddCommandAddedCallback(const CommandQueue::CommandCallback & callback)298 void ComponentManagerImpl::AddCommandAddedCallback(
299 const CommandQueue::CommandCallback& callback) {
300 command_queue_.AddCommandAddedCallback(callback);
301 }
302
AddCommandRemovedCallback(const CommandQueue::CommandCallback & callback)303 void ComponentManagerImpl::AddCommandRemovedCallback(
304 const CommandQueue::CommandCallback& callback) {
305 command_queue_.AddCommandRemovedCallback(callback);
306 }
307
AddCommandHandler(const std::string & component_path,const std::string & command_name,const Device::CommandHandlerCallback & callback)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
FindComponent(const std::string & path,ErrorPtr * error) const321 const base::DictionaryValue* ComponentManagerImpl::FindComponent(
322 const std::string& path,
323 ErrorPtr* error) const {
324 return FindComponentAt(&components_, path, error);
325 }
326
FindTraitDefinition(const std::string & name) const327 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
FindCommandDefinition(const std::string & command_name) const334 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
GetMinimalRole(const std::string & command_name,UserRole * minimal_role,ErrorPtr * error) const347 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
AddStateChangedCallback(const base::Closure & callback)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
SetStateProperties(const std::string & component_path,const base::DictionaryValue & dict,ErrorPtr * error)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
SetStatePropertiesFromJson(const std::string & component_path,const std::string & json,ErrorPtr * error)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
GetStateProperty(const std::string & component_path,const std::string & name,ErrorPtr * error) const403 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
SetStateProperty(const std::string & component_path,const std::string & name,const base::Value & value,ErrorPtr * error)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
GetAndClearRecordedStateChanges()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
NotifyStateUpdatedOnServer(UpdateID id)477 void ComponentManagerImpl::NotifyStateUpdatedOnServer(UpdateID id) {
478 on_server_state_updated_.Notify(id);
479 }
480
AddServerStateUpdatedCallback(const base::Callback<void (UpdateID)> & callback)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
FindComponentWithTrait(const std::string & trait) const488 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
AddLegacyCommandDefinitions(const base::DictionaryValue & dict,ErrorPtr * error)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
AddLegacyStateDefinitions(const base::DictionaryValue & dict,ErrorPtr * error)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
GetLegacyState() const583 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
GetLegacyCommandDefinitions() const598 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
AddTraitToLegacyComponent(const std::string & trait)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
FindComponentGraftNode(const std::string & path,ErrorPtr * error)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
FindMutableComponent(const std::string & path,ErrorPtr * error)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
FindComponentAt(const base::DictionaryValue * root,const std::string & path,ErrorPtr * error)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