| // Copyright 2015 The Weave Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/states/state_manager.h" | |
| #include <base/logging.h> | |
| #include <base/values.h> | |
| #include "src/json_error_codes.h" | |
| #include "src/states/error_codes.h" | |
| #include "src/states/state_change_queue_interface.h" | |
| #include "src/string_utils.h" | |
| #include "src/utils.h" | |
| namespace weave { | |
| StateManager::StateManager(StateChangeQueueInterface* state_change_queue) | |
| : state_change_queue_(state_change_queue) { | |
| CHECK(state_change_queue_) << "State change queue not specified"; | |
| } | |
| StateManager::~StateManager() {} | |
| void StateManager::AddChangedCallback(const base::Closure& callback) { | |
| on_changed_.push_back(callback); | |
| callback.Run(); // Force to read current state. | |
| } | |
| std::unique_ptr<base::DictionaryValue> StateManager::GetState() const { | |
| std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue}; | |
| for (const auto& pair : packages_) { | |
| auto pkg_value = pair.second->GetValuesAsJson(); | |
| CHECK(pkg_value); | |
| dict->SetWithoutPathExpansion(pair.first, pkg_value.release()); | |
| } | |
| return dict; | |
| } | |
| bool StateManager::SetProperty(const std::string& name, | |
| const base::Value& value, | |
| ErrorPtr* error) { | |
| bool result = SetPropertyValue(name, value, base::Time::Now(), error); | |
| for (const auto& cb : on_changed_) | |
| cb.Run(); | |
| return result; | |
| } | |
| std::unique_ptr<base::Value> StateManager::GetProperty( | |
| const std::string& name) const { | |
| auto parts = SplitAtFirst(name, ".", true); | |
| const std::string& package_name = parts.first; | |
| const std::string& property_name = parts.second; | |
| if (package_name.empty() || property_name.empty()) | |
| return nullptr; | |
| const StatePackage* package = FindPackage(package_name); | |
| if (!package) | |
| return nullptr; | |
| return package->GetPropertyValue(property_name, nullptr); | |
| } | |
| bool StateManager::SetPropertyValue(const std::string& full_property_name, | |
| const base::Value& value, | |
| const base::Time& timestamp, | |
| ErrorPtr* error) { | |
| auto parts = SplitAtFirst(full_property_name, ".", true); | |
| const std::string& package_name = parts.first; | |
| const std::string& property_name = parts.second; | |
| const bool split = (full_property_name.find(".") != std::string::npos); | |
| if (full_property_name.empty() || (split && property_name.empty())) { | |
| Error::AddTo(error, FROM_HERE, errors::state::kDomain, | |
| errors::state::kPropertyNameMissing, | |
| "Property name is missing"); | |
| return false; | |
| } | |
| if (!split || package_name.empty()) { | |
| Error::AddTo(error, FROM_HERE, errors::state::kDomain, | |
| errors::state::kPackageNameMissing, | |
| "Package name is missing in the property name"); | |
| return false; | |
| } | |
| StatePackage* package = FindPackage(package_name); | |
| if (package == nullptr) { | |
| Error::AddToPrintf(error, FROM_HERE, errors::state::kDomain, | |
| errors::state::kPropertyNotDefined, | |
| "Unknown state property package '%s'", | |
| package_name.c_str()); | |
| return false; | |
| } | |
| if (!package->SetPropertyValue(property_name, value, error)) | |
| return false; | |
| ValueMap prop_set{{full_property_name, package->GetProperty(property_name)}}; | |
| state_change_queue_->NotifyPropertiesUpdated(timestamp, prop_set); | |
| return true; | |
| } | |
| std::pair<StateChangeQueueInterface::UpdateID, std::vector<StateChange>> | |
| StateManager::GetAndClearRecordedStateChanges() { | |
| return std::make_pair(state_change_queue_->GetLastStateChangeId(), | |
| state_change_queue_->GetAndClearRecordedStateChanges()); | |
| } | |
| void StateManager::NotifyStateUpdatedOnServer( | |
| StateChangeQueueInterface::UpdateID id) { | |
| state_change_queue_->NotifyStateUpdatedOnServer(id); | |
| } | |
| bool StateManager::LoadStateDefinition(const base::DictionaryValue& dict, | |
| ErrorPtr* error) { | |
| for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd(); | |
| iter.Advance()) { | |
| std::string package_name = iter.key(); | |
| if (package_name.empty()) { | |
| Error::AddTo(error, FROM_HERE, errors::kErrorDomain, | |
| errors::kInvalidPackageError, "State package name is empty"); | |
| return false; | |
| } | |
| const base::DictionaryValue* package_dict = nullptr; | |
| if (!iter.value().GetAsDictionary(&package_dict)) { | |
| Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain, | |
| errors::json::kObjectExpected, | |
| "State package '%s' must be an object", | |
| package_name.c_str()); | |
| return false; | |
| } | |
| StatePackage* package = FindOrCreatePackage(package_name); | |
| CHECK(package) << "Unable to create state package " << package_name; | |
| if (!package->AddSchemaFromJson(package_dict, error)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool StateManager::LoadStateDefinitionFromJson(const std::string& json, | |
| ErrorPtr* error) { | |
| std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error); | |
| if (!dict) | |
| return false; | |
| if (!LoadStateDefinition(*dict, error)) { | |
| Error::AddToPrintf(error, FROM_HERE, errors::kErrorDomain, | |
| errors::kSchemaError, | |
| "Failed to load state definition: '%s'", json.c_str()); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool StateManager::SetProperties(const base::DictionaryValue& dict, | |
| ErrorPtr* error) { | |
| base::Time timestamp = base::Time::Now(); | |
| bool all_success = true; | |
| for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd(); | |
| iter.Advance()) { | |
| if (iter.key().empty()) { | |
| Error::AddTo(error, FROM_HERE, errors::kErrorDomain, | |
| errors::kInvalidPackageError, "State package name is empty"); | |
| all_success = false; | |
| continue; | |
| } | |
| const base::DictionaryValue* package_dict = nullptr; | |
| if (!iter.value().GetAsDictionary(&package_dict)) { | |
| Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain, | |
| errors::json::kObjectExpected, | |
| "State package '%s' must be an object", | |
| iter.key().c_str()); | |
| all_success = false; | |
| continue; | |
| } | |
| for (base::DictionaryValue::Iterator it_prop(*package_dict); | |
| !it_prop.IsAtEnd(); it_prop.Advance()) { | |
| if (!SetPropertyValue(iter.key() + "." + it_prop.key(), it_prop.value(), | |
| timestamp, error)) { | |
| all_success = false; | |
| continue; | |
| } | |
| } | |
| } | |
| for (const auto& cb : on_changed_) | |
| cb.Run(); | |
| return all_success; | |
| } | |
| bool StateManager::SetPropertiesFromJson(const std::string& json, | |
| ErrorPtr* error) { | |
| std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error); | |
| if (!dict) | |
| return false; | |
| if (!SetProperties(*dict, error)) { | |
| Error::AddToPrintf(error, FROM_HERE, errors::kErrorDomain, | |
| errors::kSchemaError, "Failed to load defaults: '%s'", | |
| json.c_str()); | |
| return false; | |
| } | |
| return true; | |
| } | |
| StatePackage* StateManager::FindPackage(const std::string& package_name) { | |
| auto it = packages_.find(package_name); | |
| return (it != packages_.end()) ? it->second.get() : nullptr; | |
| } | |
| const StatePackage* StateManager::FindPackage( | |
| const std::string& package_name) const { | |
| auto it = packages_.find(package_name); | |
| return (it != packages_.end()) ? it->second.get() : nullptr; | |
| } | |
| StatePackage* StateManager::FindOrCreatePackage( | |
| const std::string& package_name) { | |
| StatePackage* package = FindPackage(package_name); | |
| if (package == nullptr) { | |
| std::unique_ptr<StatePackage> new_package{new StatePackage(package_name)}; | |
| package = packages_.emplace(package_name, std::move(new_package)) | |
| .first->second.get(); | |
| } | |
| return package; | |
| } | |
| } // namespace weave |