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